- bleder features separated between different packages - refactoring of modifier system (each modifier now in a separate class) - refactoring of constraints system (each constraint now in a separate class) - no converter interface - every helper supports versions 2.49 and 2.5+. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7953 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
21f09ad716
commit
1e7b7860fe
@ -1,8 +1,9 @@ |
||||
package com.jme3.scene.plugins.blender.structures; |
||||
package com.jme3.scene.plugins.blender.animations; |
||||
|
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.curves.BezierCurve; |
||||
|
||||
/** |
||||
* This class is used to calculate bezier curves value for the given frames. The Ipo (interpolation object) consists |
@ -1,15 +1,14 @@ |
||||
package com.jme3.scene.plugins.blender.helpers.v249; |
||||
package com.jme3.scene.plugins.blender.animations; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.structures.BezierCurve; |
||||
import com.jme3.scene.plugins.blender.structures.Ipo; |
||||
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.curves.BezierCurve; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Pointer; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class helps to compute values from interpolation curves for features like animation or constraint influence. The |
@ -0,0 +1,118 @@ |
||||
package com.jme3.scene.plugins.blender.cameras; |
||||
|
||||
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.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* A class that is used to load cameras into the scene. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class CameraHelper extends AbstractBlenderHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); |
||||
protected static final int DEFAULT_CAM_WIDTH = 100; |
||||
protected static final int DEFAULT_CAM_HEIGHT = 100; |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public CameraHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
/** |
||||
* This method converts the given structure to jme camera. |
||||
* |
||||
* @param structure |
||||
* camera structure |
||||
* @return jme camera object |
||||
* @throws BlenderFileException |
||||
* an exception is thrown when there are problems with the |
||||
* blender file |
||||
*/ |
||||
public Camera toCamera(Structure structure) throws BlenderFileException { |
||||
if (blenderVersion >= 250) { |
||||
return this.toCamera250(structure); |
||||
} else { |
||||
return this.toCamera249(structure); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method converts the given structure to jme camera. Should be used form blender 2.5+. |
||||
* |
||||
* @param structure |
||||
* camera structure |
||||
* @return jme camera object |
||||
* @throws BlenderFileException |
||||
* an exception is thrown when there are problems with the |
||||
* blender file |
||||
*/ |
||||
public Camera toCamera250(Structure structure) throws BlenderFileException { |
||||
Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); |
||||
int type = ((Number) structure.getFieldValue("type")).intValue(); |
||||
if (type != 0 && type != 1) { |
||||
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); |
||||
type = 0; |
||||
} |
||||
//type==0 - perspective; type==1 - orthographic; perspective is used as default
|
||||
result.setParallelProjection(type == 1); |
||||
float aspect = 0; |
||||
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); |
||||
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); |
||||
if (type == 0) { |
||||
aspect = ((Number) structure.getFieldValue("lens")).floatValue(); |
||||
} else { |
||||
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); |
||||
} |
||||
result.setFrustumPerspective(45, aspect, clipsta, clipend); |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* This method converts the given structure to jme camera. Should be used form blender 2.49. |
||||
* |
||||
* @param structure |
||||
* camera structure |
||||
* @return jme camera object |
||||
* @throws BlenderFileException |
||||
* an exception is thrown when there are problems with the |
||||
* blender file |
||||
*/ |
||||
public Camera toCamera249(Structure structure) throws BlenderFileException { |
||||
Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); |
||||
int type = ((Number) structure.getFieldValue("type")).intValue(); |
||||
if (type != 0 && type != 1) { |
||||
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); |
||||
type = 0; |
||||
} |
||||
//type==0 - perspective; type==1 - orthographic; perspective is used as default
|
||||
result.setParallelProjection(type == 1); |
||||
float angle = ((Number) structure.getFieldValue("angle")).floatValue(); |
||||
float aspect = 0; |
||||
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); |
||||
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); |
||||
if (type == 0) { |
||||
aspect = ((Number) structure.getFieldValue("lens")).floatValue(); |
||||
} else { |
||||
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); |
||||
} |
||||
result.setFrustumPerspective(angle, aspect, clipsta, clipend); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) { |
||||
return (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0; |
||||
} |
||||
} |
@ -0,0 +1,247 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Pointer; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
import com.jme3.scene.plugins.blender.objects.ObjectHelper; |
||||
|
||||
/** |
||||
* The implementation of a constraint. |
||||
* |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public abstract class Constraint { |
||||
|
||||
/** The name of this constraint. */ |
||||
protected final String name; |
||||
/** The old memory address of the constraint's owner. */ |
||||
protected final Long boneOMA; |
||||
protected final Space ownerSpace; |
||||
protected final Space targetSpace; |
||||
/** The structure with constraint's data. */ |
||||
protected final Structure data; |
||||
/** The ipo object defining influence. */ |
||||
protected final Ipo ipo; |
||||
protected DataRepository dataRepository; |
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public Constraint(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
this.name = constraintStructure.getFieldValue("name").toString(); |
||||
ConstraintType constraintType = ConstraintType.valueOf(((Number)constraintStructure.getFieldValue("type")).intValue()); |
||||
if(constraintType != this.getType()) { |
||||
throw new IllegalStateException("Constraint structure does not match its type for constraint: " + name); |
||||
} |
||||
Pointer pData = (Pointer) constraintStructure.getFieldValue("data"); |
||||
if (pData.isNotNull()) { |
||||
data = pData.fetchData(dataRepository.getInputStream()).get(0); |
||||
} else { |
||||
throw new BlenderFileException("The constraint has no data specified!"); |
||||
} |
||||
this.boneOMA = boneOMA; |
||||
this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue()); |
||||
this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue()); |
||||
this.ipo = influenceIpo; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the name of the constraint. |
||||
* |
||||
* @return the name of the constraint |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the old memoty address of the bone this constraint |
||||
* affects. |
||||
* |
||||
* @return the old memory address of the bone this constraint affects |
||||
*/ |
||||
public Long getBoneOMA() { |
||||
return boneOMA; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the type of the constraint. |
||||
* |
||||
* @return the type of the constraint |
||||
*/ |
||||
public abstract ConstraintType getType(); |
||||
|
||||
/** |
||||
* This method returns the bone traces for the bone that is affected by the given constraint. |
||||
* @param skeleton |
||||
* the skeleton containing bones |
||||
* @param boneAnimation |
||||
* the bone animation that affects the skeleton |
||||
* @return the bone track for the bone that is being affected by the constraint |
||||
*/ |
||||
protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
Bone bone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
int boneIndex = bone==null ? 0 : skeleton.getBoneIndex(bone);//bone==null may mean the object animation
|
||||
if (boneIndex != -1) { |
||||
//searching for track for this bone
|
||||
for (BoneTrack boneTrack : boneAnimation.getTracks()) { |
||||
if (boneTrack.getTargetBoneIndex() == boneIndex) { |
||||
return boneTrack; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the target or subtarget object (if specified). |
||||
* @param loadedFeatureDataType |
||||
* @return target or subtarget feature |
||||
* @throws BlenderFileException this exception is thrown if the blend file is somehow corrupted |
||||
*/ |
||||
protected Object getTarget(LoadedFeatureDataType loadedFeatureDataType) throws BlenderFileException { |
||||
//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); |
||||
//always load the target first
|
||||
Long targetOMA = ((Pointer) data.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 = data.getFieldValue("subtarget"); |
||||
String subtargetName = subtarget==null ? null : subtarget.toString(); |
||||
if (subtargetName!=null && subtargetName.length() > 0) { |
||||
result = dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object location. |
||||
* @return target's object location |
||||
*/ |
||||
protected Vector3f getTargetLocation() { |
||||
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress(); |
||||
Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
switch (targetSpace) { |
||||
case CONSTRAINT_SPACE_LOCAL: |
||||
return targetObject.getLocalTranslation(); |
||||
case CONSTRAINT_SPACE_WORLD: |
||||
return targetObject.getWorldTranslation(); |
||||
default: |
||||
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object location in the specified frame. |
||||
* @param frame |
||||
* the frame number |
||||
* @return target's object location |
||||
*/ |
||||
protected Vector3f getTargetLocation(int frame) { |
||||
return this.getTargetLocation();//TODO: implement getting location in a specified frame
|
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object rotation. |
||||
* @return target's object rotation |
||||
*/ |
||||
protected Quaternion getTargetRotation() { |
||||
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress(); |
||||
Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
switch (targetSpace) { |
||||
case CONSTRAINT_SPACE_LOCAL: |
||||
return targetObject.getLocalRotation(); |
||||
case CONSTRAINT_SPACE_WORLD: |
||||
return targetObject.getWorldRotation(); |
||||
default: |
||||
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object scale. |
||||
* @return target's object scale |
||||
*/ |
||||
protected Vector3f getTargetScale() { |
||||
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress(); |
||||
Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
switch (targetSpace) { |
||||
case CONSTRAINT_SPACE_LOCAL: |
||||
return targetObject.getLocalScale(); |
||||
case CONSTRAINT_SPACE_WORLD: |
||||
return targetObject.getWorldScale(); |
||||
default: |
||||
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method affects the bone animation tracks for the given skeleton. |
||||
* |
||||
* @param skeleton |
||||
* the skeleton containing the affected bones by constraint |
||||
* @param boneAnimation |
||||
* the bone animation baked traces |
||||
* @param constraint |
||||
* the constraint |
||||
*/ |
||||
public abstract void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation); |
||||
|
||||
/** |
||||
* The space of target or owner transformation. |
||||
* |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public static enum Space { |
||||
|
||||
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID; |
||||
|
||||
/** |
||||
* This method returns the enum instance when given the appropriate |
||||
* value from the blend file. |
||||
* |
||||
* @param c |
||||
* the blender's value of the space modifier |
||||
* @return the scape enum instance |
||||
*/ |
||||
public static Space valueOf(byte c) { |
||||
switch (c) { |
||||
case 0: |
||||
return CONSTRAINT_SPACE_WORLD; |
||||
case 1: |
||||
return CONSTRAINT_SPACE_LOCAL; |
||||
case 2: |
||||
return CONSTRAINT_SPACE_POSE; |
||||
case 3: |
||||
return CONSTRAINT_SPACE_PARLOCAL; |
||||
default: |
||||
return CONSTRAINT_SPACE_INVALID; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Action' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintAction extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintAction.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintAction(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Action' constraint
|
||||
LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_ACTION; |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'ChildOf' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintChildOf extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintChildOf.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintChildOf(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement ChildOf constraint
|
||||
LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_CHILDOF; |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Clamp to' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintClampTo extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintClampTo.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintClampTo(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) |
||||
throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
//TODO: implement when curves are implemented
|
||||
LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", this.getName()); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_CLAMPTO; |
||||
} |
||||
} |
@ -0,0 +1,84 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Dist limit' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintDistLimit extends Constraint { |
||||
private static final int LIMITDIST_INSIDE = 0; |
||||
private static final int LIMITDIST_OUTSIDE = 1; |
||||
private static final int LIMITDIST_ONSURFACE = 2; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintDistLimit(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
Vector3f targetLocation = this.getTargetLocation(); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
//TODO: target vertex group !!!
|
||||
float dist = ((Number) data.getFieldValue("dist")).floatValue(); |
||||
int mode = ((Number) data.getFieldValue("mode")).intValue(); |
||||
|
||||
int maxFrames = boneTrack.getTimes().length; |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f v = translations[frame].subtract(targetLocation); |
||||
float currentDistance = v.length(); |
||||
float influence = ipo.calculateValue(frame); |
||||
float modifier = 0.0f; |
||||
switch (mode) { |
||||
case LIMITDIST_INSIDE: |
||||
if (currentDistance >= dist) { |
||||
modifier = (dist - currentDistance) / currentDistance; |
||||
} |
||||
break; |
||||
case LIMITDIST_ONSURFACE: |
||||
modifier = (dist - currentDistance) / currentDistance; |
||||
break; |
||||
case LIMITDIST_OUTSIDE: |
||||
if (currentDistance <= dist) { |
||||
modifier = (dist - currentDistance) / currentDistance; |
||||
} |
||||
break; |
||||
default: |
||||
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode); |
||||
} |
||||
translations[frame].addLocal(v.multLocal(modifier * influence)); |
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_DISTLIMIT; |
||||
} |
||||
} |
@ -0,0 +1,78 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* A factory class to create new instances of constraints depending on the type from the constraint's structure. |
||||
* This class has a package scope. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ final class ConstraintFactory { |
||||
|
||||
/** |
||||
* This method creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public static Constraint createConstraint(Structure constraintStructure, Long boneOMA, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
int type = ((Number)constraintStructure.getFieldValue("type")).intValue(); |
||||
ConstraintType constraintType = ConstraintType.valueOf(type); |
||||
switch(constraintType) { |
||||
case CONSTRAINT_TYPE_ACTION: |
||||
return new ConstraintAction(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_CHILDOF: |
||||
return new ConstraintChildOf(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_CLAMPTO: |
||||
return new ConstraintClampTo(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_DISTLIMIT: |
||||
return new ConstraintDistLimit(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_FOLLOWPATH: |
||||
return new ConstraintFollowPath(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_KINEMATIC: |
||||
return new ConstraintInverseKinematics(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_LOCKTRACK: |
||||
return new ConstraintLockTrack(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_LOCLIKE: |
||||
return new ConstraintLocLike(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_LOCLIMIT: |
||||
return new ConstraintLocLimit(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_MINMAX: |
||||
return new ConstraintMinMax(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_NULL: |
||||
return new ConstraintNull(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_PYTHON: |
||||
return new ConstraintPython(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_RIGIDBODYJOINT: |
||||
return new ConstraintRigidBodyJoint(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_ROTLIKE: |
||||
return new ConstraintRotLike(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_ROTLIMIT: |
||||
return new ConstraintRotLimit(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_SHRINKWRAP: |
||||
return new ConstraintShrinkWrap(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_SIZELIKE: |
||||
return new ConstraintSizeLike(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_SIZELIMIT: |
||||
return new ConstraintSizeLimit(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_STRETCHTO: |
||||
return new ConstraintStretchTo(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
case CONSTRAINT_TYPE_TRANSFORM: |
||||
return new ConstraintTransform(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
default: |
||||
throw new IllegalStateException("Unknown constraint type: " + constraintType); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Follow path' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintFollowPath extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintFollowPath.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintFollowPath(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
//TODO: implement when curves are implemented
|
||||
LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH; |
||||
} |
||||
} |
@ -0,0 +1,140 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.animations.IpoHelper; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Pointer; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class should be used for constraint calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ConstraintHelper extends AbstractBlenderHelper { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName()); |
||||
|
||||
/** |
||||
* Helper constructor. It's main task is to generate the affection functions. These functions are common to all |
||||
* ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall |
||||
* consider refactoring. The constructor parses the given blender version and stores the result. Some |
||||
* functionalities may differ in different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
/** |
||||
* This method reads constraints for for the given structure. The constraints are loaded only once for object/bone. |
||||
* @param ownerOMA |
||||
* the owner's old memory address |
||||
* @param objectStructure |
||||
* the structure we read constraint's for |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
*/ |
||||
public Map<Long, List<Constraint>> loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
if (blenderVersion < 250) {//TODO
|
||||
LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !"); |
||||
return new HashMap<Long, List<Constraint>>(0); |
||||
} |
||||
|
||||
// reading influence ipos for the constraints
|
||||
IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); |
||||
Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>(); |
||||
Pointer pActions = (Pointer) objectStructure.getFieldValue("action"); |
||||
if (pActions.isNotNull()) { |
||||
List<Structure> actions = pActions.fetchData(dataRepository.getInputStream()); |
||||
for (Structure action : actions) { |
||||
Structure chanbase = (Structure) action.getFieldValue("chanbase"); |
||||
List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository); |
||||
for (Structure actionChannel : actionChannels) { |
||||
Map<String, Ipo> ipos = new HashMap<String, Ipo>(); |
||||
Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels"); |
||||
List<Structure> constraintChannels = constChannels.evaluateListBase(dataRepository); |
||||
for (Structure constraintChannel : constraintChannels) { |
||||
Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo"); |
||||
if (pIpo.isNotNull()) { |
||||
String constraintName = constraintChannel.getFieldValue("name").toString(); |
||||
Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository); |
||||
ipos.put(constraintName, ipo); |
||||
} |
||||
} |
||||
String actionName = actionChannel.getFieldValue("name").toString(); |
||||
constraintsIpos.put(actionName, ipos); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Map<Long, List<Constraint>> result = new HashMap<Long, List<Constraint>>(); |
||||
|
||||
//loading constraints connected with the object's bones
|
||||
Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
|
||||
if (pPose.isNotNull()) { |
||||
List<Structure> poseChannels = ((Structure) pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository); |
||||
for (Structure poseChannel : poseChannels) { |
||||
List<Constraint> constraintsList = new ArrayList<Constraint>(); |
||||
Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress()); |
||||
|
||||
//the name is read directly from structure because bone might not yet be loaded
|
||||
String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString(); |
||||
List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository); |
||||
for (Structure constraint : constraints) { |
||||
String constraintName = constraint.getFieldValue("name").toString(); |
||||
Map<String, Ipo> ipoMap = constraintsIpos.get(name); |
||||
Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName); |
||||
if (ipo == null) { |
||||
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); |
||||
ipo = ipoHelper.createIpo(enforce); |
||||
} |
||||
constraintsList.add(ConstraintFactory.createConstraint(constraint, boneOMA, ipo, dataRepository)); |
||||
} |
||||
|
||||
result.put(boneOMA, constraintsList); |
||||
dataRepository.addConstraints(boneOMA, constraintsList); |
||||
} |
||||
} |
||||
// TODO: reading constraints for objects (implement when object's animation will be available)
|
||||
List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository); |
||||
for(Structure constraintChannel : constraintChannels) { |
||||
System.out.println(constraintChannel); |
||||
} |
||||
|
||||
//loading constraints connected with the object itself (TODO: test this)
|
||||
if(!result.containsKey(objectStructure.getOldMemoryAddress())) { |
||||
List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository); |
||||
List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size()); |
||||
|
||||
for(Structure constraint : constraints) { |
||||
String constraintName = constraint.getFieldValue("name").toString(); |
||||
String objectName = objectStructure.getName(); |
||||
|
||||
Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName); |
||||
Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null; |
||||
if (ipo == null) { |
||||
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); |
||||
ipo = ipoHelper.createIpo(enforce); |
||||
} |
||||
constraintsList.add(ConstraintFactory.createConstraint(constraint, null, ipo, dataRepository)); |
||||
} |
||||
result.put(objectStructure.getOldMemoryAddress(), constraintsList); |
||||
dataRepository.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,170 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
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.Node; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.animations.CalculationBone; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
import com.jme3.scene.plugins.blender.objects.ObjectHelper; |
||||
|
||||
/** |
||||
* This class represents 'Inverse kinematics' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintInverseKinematics extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintInverseKinematics.class.getName()); |
||||
private static final float IK_SOLVER_ERROR = 0.5f; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintInverseKinematics(Structure constraintStructure, |
||||
Long boneOMA, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
try { |
||||
// IK solver is only attached to bones
|
||||
Bone ownerBone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
|
||||
// get the target point
|
||||
Object targetObject = this.getTarget(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(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) data.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(); |
||||
|
||||
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; |
||||
} |
||||
} |
||||
|
||||
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("===============================");
|
||||
// }
|
||||
} catch(BlenderFileException e) { |
||||
LOGGER.severe(e.getLocalizedMessage()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns bones used for rotation calculations. |
||||
* @param bone |
||||
* the bone to which the constraint is applied |
||||
* @param skeleton |
||||
* the skeleton owning the bone and its ancestors |
||||
* @param boneAnimation |
||||
* the bone animation data that stores the traces for the skeleton's bones |
||||
* @return a list of bones to imitate the bone's movement during IK solving |
||||
*/ |
||||
private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
List<CalculationBone> bonesList = new ArrayList<CalculationBone>(); |
||||
Bone currentBone = bone; |
||||
do { |
||||
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
|
||||
CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]); |
||||
for (int i = result.length - 1; i > 0; --i) { |
||||
result[i].attachChild(result[i - 1]); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_KINEMATIC; |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Loc like' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintLocLike extends Constraint { |
||||
private static final int LOCLIKE_X = 0x01; |
||||
private static final int LOCLIKE_Y = 0x02; |
||||
private static final int LOCLIKE_Z = 0x04; |
||||
/* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */ |
||||
//protected static final int LOCLIKE_TIP = 0x08;
|
||||
private static final int LOCLIKE_X_INVERT = 0x10; |
||||
private static final int LOCLIKE_Y_INVERT = 0x20; |
||||
private static final int LOCLIKE_Z_INVERT = 0x40; |
||||
private static final int LOCLIKE_OFFSET = 0x80; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintLocLike(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
Vector3f targetLocation = this.getTargetLocation(); |
||||
int flag = ((Number) data.getFieldValue("flag")).intValue(); |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
int maxFrames = translations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f offset = Vector3f.ZERO; |
||||
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
|
||||
offset = translations[frame].clone(); |
||||
} |
||||
|
||||
if ((flag & LOCLIKE_X) != 0) { |
||||
translations[frame].x = targetLocation.x; |
||||
if ((flag & LOCLIKE_X_INVERT) != 0) { |
||||
translations[frame].x = -translations[frame].x; |
||||
} |
||||
} else if ((flag & LOCLIKE_Y) != 0) { |
||||
translations[frame].y = targetLocation.y; |
||||
if ((flag & LOCLIKE_Y_INVERT) != 0) { |
||||
translations[frame].y = -translations[frame].y; |
||||
} |
||||
} else if ((flag & LOCLIKE_Z) != 0) { |
||||
translations[frame].z = targetLocation.z; |
||||
if ((flag & LOCLIKE_Z_INVERT) != 0) { |
||||
translations[frame].z = -translations[frame].z; |
||||
} |
||||
} |
||||
translations[frame].addLocal(offset);//TODO: ipo influence
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_LOCLIKE; |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Loc limit' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintLocLimit extends Constraint { |
||||
private static final int LIMIT_XMIN = 0x01; |
||||
private static final int LIMIT_XMAX = 0x02; |
||||
private static final int LIMIT_YMIN = 0x04; |
||||
private static final int LIMIT_YMAX = 0x08; |
||||
private static final int LIMIT_ZMIN = 0x10; |
||||
private static final int LIMIT_ZMAX = 0x20; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintLocLimit(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) data.getFieldValue("flag")).intValue(); |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
int maxFrames = translations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float influence = ipo.calculateValue(frame); |
||||
if ((flag & LIMIT_XMIN) != 0) { |
||||
float xmin = ((Number) data.getFieldValue("xmin")).floatValue(); |
||||
if (translations[frame].x < xmin) { |
||||
translations[frame].x -= (translations[frame].x - xmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_XMAX) != 0) { |
||||
float xmax = ((Number) data.getFieldValue("xmax")).floatValue(); |
||||
if (translations[frame].x > xmax) { |
||||
translations[frame].x -= (translations[frame].x - xmax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMIN) != 0) { |
||||
float ymin = ((Number) data.getFieldValue("ymin")).floatValue(); |
||||
if (translations[frame].y < ymin) { |
||||
translations[frame].y -= (translations[frame].y - ymin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMAX) != 0) { |
||||
float ymax = ((Number) data.getFieldValue("ymax")).floatValue(); |
||||
if (translations[frame].y > ymax) { |
||||
translations[frame].y -= (translations[frame].y - ymax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMIN) != 0) { |
||||
float zmin = ((Number) data.getFieldValue("zmin")).floatValue(); |
||||
if (translations[frame].z < zmin) { |
||||
translations[frame].z -= (translations[frame].z - zmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMAX) != 0) { |
||||
float zmax = ((Number) data.getFieldValue("zmax")).floatValue(); |
||||
if (translations[frame].z > zmax) { |
||||
translations[frame].z -= (translations[frame].z - zmax) * influence; |
||||
} |
||||
}//TODO: consider constraint space !!!
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_LOCLIMIT; |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Action' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintLockTrack extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintLockTrack.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintLockTrack(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) |
||||
throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Lock track' constraint
|
||||
LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_LOCKTRACK; |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Min max' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintMinMax extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintMinMax.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintMinMax(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Min max' constraint
|
||||
LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_MINMAX; |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Null' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintNull extends Constraint { |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintNull(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) |
||||
throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) {} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_NULL; |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Python' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintPython extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintPython.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintPython(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Python' constraint
|
||||
LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_PYTHON; |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Rigid body joint' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintRigidBodyJoint extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintRigidBodyJoint.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintRigidBodyJoint(Structure constraintStructure, |
||||
Long boneOMA, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Rigid body joint' constraint
|
||||
LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT; |
||||
} |
||||
} |
@ -0,0 +1,88 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Rot like' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintRotLike extends Constraint { |
||||
private static final int ROTLIKE_X = 0x01; |
||||
private static final int ROTLIKE_Y = 0x02; |
||||
private static final int ROTLIKE_Z = 0x04; |
||||
private static final int ROTLIKE_X_INVERT = 0x10; |
||||
private static final int ROTLIKE_Y_INVERT = 0x20; |
||||
private static final int ROTLIKE_Z_INVERT = 0x40; |
||||
private static final int ROTLIKE_OFFSET = 0x80; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintRotLike(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
Quaternion targetRotation = this.getTargetRotation(); |
||||
int flag = ((Number) data.getFieldValue("flag")).intValue(); |
||||
float[] targetAngles = targetRotation.toAngles(null); |
||||
Quaternion[] rotations = boneTrack.getRotations(); |
||||
int maxFrames = rotations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float[] angles = rotations[frame].toAngles(null); |
||||
|
||||
Quaternion offset = Quaternion.IDENTITY; |
||||
if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
|
||||
offset = rotations[frame].clone(); |
||||
} |
||||
|
||||
if ((flag & ROTLIKE_X) != 0) { |
||||
angles[0] = targetAngles[0]; |
||||
if ((flag & ROTLIKE_X_INVERT) != 0) { |
||||
angles[0] = -angles[0]; |
||||
} |
||||
} else if ((flag & ROTLIKE_Y) != 0) { |
||||
angles[1] = targetAngles[1]; |
||||
if ((flag & ROTLIKE_Y_INVERT) != 0) { |
||||
angles[1] = -angles[1]; |
||||
} |
||||
} else if ((flag & ROTLIKE_Z) != 0) { |
||||
angles[2] = targetAngles[2]; |
||||
if ((flag & ROTLIKE_Z_INVERT) != 0) { |
||||
angles[2] = -angles[2]; |
||||
} |
||||
} |
||||
rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_ROTLIKE; |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.FastMath; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Rot limit' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintRotLimit extends Constraint { |
||||
private static final int LIMIT_XROT = 0x01; |
||||
private static final int LIMIT_YROT = 0x02; |
||||
private static final int LIMIT_ZROT = 0x04; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintRotLimit(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) data.getFieldValue("flag")).intValue(); |
||||
Quaternion[] rotations = boneTrack.getRotations(); |
||||
int maxFrames = rotations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float[] angles = rotations[frame].toAngles(null); |
||||
float influence = ipo.calculateValue(frame); |
||||
if ((flag & LIMIT_XROT) != 0) { |
||||
float xmin = ((Number) data.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float xmax = ((Number) data.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float difference = 0.0f; |
||||
if (angles[0] < xmin) { |
||||
difference = (angles[0] - xmin) * influence; |
||||
} else if (angles[0] > xmax) { |
||||
difference = (angles[0] - xmax) * influence; |
||||
} |
||||
angles[0] -= difference; |
||||
} |
||||
if ((flag & LIMIT_YROT) != 0) { |
||||
float ymin = ((Number) data.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float ymax = ((Number) data.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float difference = 0.0f; |
||||
if (angles[1] < ymin) { |
||||
difference = (angles[1] - ymin) * influence; |
||||
} else if (angles[1] > ymax) { |
||||
difference = (angles[1] - ymax) * influence; |
||||
} |
||||
angles[1] -= difference; |
||||
} |
||||
if ((flag & LIMIT_ZROT) != 0) { |
||||
float zmin = ((Number) data.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float zmax = ((Number) data.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float difference = 0.0f; |
||||
if (angles[2] < zmin) { |
||||
difference = (angles[2] - zmin) * influence; |
||||
} else if (angles[2] > zmax) { |
||||
difference = (angles[2] - zmax) * influence; |
||||
} |
||||
angles[2] -= difference; |
||||
} |
||||
rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_ROTLIMIT; |
||||
} |
||||
} |
@ -0,0 +1,100 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.nio.FloatBuffer; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.VertexBuffer.Type; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Shrink wrap' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintShrinkWrap extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintShrinkWrap.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintShrinkWrap(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
//loading mesh points (blender ensures that the target is a mesh-object)
|
||||
List<Vector3f> pts = new ArrayList<Vector3f>(); |
||||
try { |
||||
Node node = (Node)this.getTarget(LoadedFeatureDataType.LOADED_FEATURE); |
||||
for(Spatial spatial : node.getChildren()) { |
||||
if(spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position); |
||||
for(int i=0;i<floatBuffer.limit();i+=3) { |
||||
pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//modifying traces
|
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
Quaternion[] rotations = boneTrack.getRotations(); |
||||
int maxFrames = translations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f currentTranslation = translations[frame]; |
||||
|
||||
//looking for minimum distanced point
|
||||
Vector3f minDistancePoint = null; |
||||
float distance = Float.MAX_VALUE; |
||||
for(Vector3f p : pts) { |
||||
float temp = currentTranslation.distance(p); |
||||
if(temp < distance) { |
||||
distance = temp; |
||||
minDistancePoint = p; |
||||
} |
||||
} |
||||
translations[frame] = minDistancePoint.clone(); |
||||
} |
||||
|
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, rotations, boneTrack.getScales()); |
||||
} |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.severe(e.getLocalizedMessage()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP; |
||||
} |
||||
} |
@ -0,0 +1,74 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Size like' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintSizeLike extends Constraint { |
||||
private static final int SIZELIKE_X = 0x01; |
||||
private static final int SIZELIKE_Y = 0x02; |
||||
private static final int SIZELIKE_Z = 0x04; |
||||
private static final int LOCLIKE_OFFSET = 0x80; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintSizeLike(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
Vector3f targetScale = this.getTargetLocation(); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) data.getFieldValue("flag")).intValue(); |
||||
Vector3f[] scales = boneTrack.getScales(); |
||||
int maxFrames = scales.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f offset = Vector3f.ZERO; |
||||
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
|
||||
offset = scales[frame].clone(); |
||||
} |
||||
|
||||
if ((flag & SIZELIKE_X) != 0) { |
||||
scales[frame].x = targetScale.x; |
||||
} else if ((flag & SIZELIKE_Y) != 0) { |
||||
scales[frame].y = targetScale.y; |
||||
} else if ((flag & SIZELIKE_Z) != 0) { |
||||
scales[frame].z = targetScale.z; |
||||
} |
||||
scales[frame].addLocal(offset);//TODO: ipo influence
|
||||
//TODO: add or multiply???
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_SIZELIKE; |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Size limit' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintSizeLimit extends Constraint { |
||||
private static final int LIMIT_XMIN = 0x01; |
||||
private static final int LIMIT_XMAX = 0x02; |
||||
private static final int LIMIT_YMIN = 0x04; |
||||
private static final int LIMIT_YMAX = 0x08; |
||||
private static final int LIMIT_ZMIN = 0x10; |
||||
private static final int LIMIT_ZMAX = 0x20; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintSizeLimit(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) data.getFieldValue("flag")).intValue(); |
||||
Vector3f[] scales = boneTrack.getScales(); |
||||
int maxFrames = scales.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float influence = ipo.calculateValue(frame); |
||||
if ((flag & LIMIT_XMIN) != 0) { |
||||
float xmin = ((Number) data.getFieldValue("xmin")).floatValue(); |
||||
if (scales[frame].x < xmin) { |
||||
scales[frame].x -= (scales[frame].x - xmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_XMAX) != 0) { |
||||
float xmax = ((Number) data.getFieldValue("xmax")).floatValue(); |
||||
if (scales[frame].x > xmax) { |
||||
scales[frame].x -= (scales[frame].x - xmax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMIN) != 0) { |
||||
float ymin = ((Number) data.getFieldValue("ymin")).floatValue(); |
||||
if (scales[frame].y < ymin) { |
||||
scales[frame].y -= (scales[frame].y - ymin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMAX) != 0) { |
||||
float ymax = ((Number) data.getFieldValue("ymax")).floatValue(); |
||||
if (scales[frame].y > ymax) { |
||||
scales[frame].y -= (scales[frame].y - ymax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMIN) != 0) { |
||||
float zmin = ((Number) data.getFieldValue("zmin")).floatValue(); |
||||
if (scales[frame].z < zmin) { |
||||
scales[frame].z -= (scales[frame].z - zmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMAX) != 0) { |
||||
float zmax = ((Number) data.getFieldValue("zmax")).floatValue(); |
||||
if (scales[frame].z > zmax) { |
||||
scales[frame].z -= (scales[frame].z - zmax) * influence; |
||||
} |
||||
}//TODO: consider constraint space !!!
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_SIZELIMIT; |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Stretch to' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintStretchTo extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintStretchTo.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintStretchTo(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) |
||||
throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Stretch to' constraint
|
||||
LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_STRETCHTO; |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* This class represents 'Transform' constraint type in blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ConstraintTransform extends Constraint { |
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintAction.class.getName()); |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ConstraintTransform(Structure constraintStructure, Long boneOMA, |
||||
Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
super(constraintStructure, boneOMA, influenceIpo, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
// TODO: implement 'Transform' constraint
|
||||
LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!"); |
||||
} |
||||
|
||||
@Override |
||||
public ConstraintType getType() { |
||||
return ConstraintType.CONSTRAINT_TYPE_TRANSFORM; |
||||
} |
||||
} |
@ -1,4 +1,4 @@ |
||||
package com.jme3.scene.plugins.blender.structures; |
||||
package com.jme3.scene.plugins.blender.constraints; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
@ -1,11 +1,11 @@ |
||||
package com.jme3.scene.plugins.blender.structures; |
||||
package com.jme3.scene.plugins.blender.curves; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.utils.DynamicArray; |
||||
import com.jme3.scene.plugins.blender.file.DynamicArray; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize |
@ -1,14 +1,11 @@ |
||||
package com.jme3.scene.plugins.blender.data; |
||||
package com.jme3.scene.plugins.blender.file; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import com.jme3.scene.plugins.blender.data.Structure.DataType; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.utils.BlenderInputStream; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.DynamicArray; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Structure.DataType; |
||||
|
||||
/** |
||||
* This class represents a single field in the structure. It can be either a primitive type or a table or a reference to |
@ -1,132 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.scene.plugins.blender.data.FileBlockHeader; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.structures.BezierCurve; |
||||
import com.jme3.scene.plugins.blender.structures.Ipo; |
||||
import com.jme3.scene.plugins.blender.utils.BlenderInputStream; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
|
||||
/** |
||||
* This class defines the methods to calculate certain aspects of animation and armature functionalities. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ArmatureHelper extends com.jme3.scene.plugins.blender.helpers.v249.ArmatureHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName()); |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ArmatureHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
@Override |
||||
public BoneTrack[] getTracks(Structure actionStructure, DataRepository dataRepository, String objectName, String animationName) throws BlenderFileException { |
||||
if (blenderVersion < 250) { |
||||
return super.getTracks(actionStructure, dataRepository, objectName, animationName); |
||||
} |
||||
LOGGER.log(Level.INFO, "Getting tracks!"); |
||||
int fps = dataRepository.getBlenderKey().getFps(); |
||||
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, animationName); |
||||
Structure groups = (Structure) actionStructure.getFieldValue("groups"); |
||||
List<Structure> actionGroups = groups.evaluateListBase(dataRepository);//bActionGroup
|
||||
if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { |
||||
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); |
||||
} |
||||
|
||||
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); |
||||
for (Structure actionGroup : actionGroups) { |
||||
String name = actionGroup.getFieldValue("name").toString(); |
||||
Integer boneIndex = bonesMap.get(name); |
||||
if (boneIndex != null) { |
||||
List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(dataRepository); |
||||
BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; |
||||
int channelCounter = 0; |
||||
for (Structure c : channels) { |
||||
//reading rna path first
|
||||
BlenderInputStream bis = dataRepository.getInputStream(); |
||||
int currentPosition = bis.getPosition(); |
||||
Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path"); |
||||
FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pRnaPath.getOldMemoryAddress()); |
||||
bis.setPosition(dataFileBlock.getBlockPosition()); |
||||
String rnaPath = bis.readString(); |
||||
bis.setPosition(currentPosition); |
||||
int arrayIndex = ((Number) c.getFieldValue("array_index")).intValue(); |
||||
int type = this.getCurveType(rnaPath, arrayIndex); |
||||
|
||||
Pointer pBezTriple = (Pointer) c.getFieldValue("bezt"); |
||||
List<Structure> bezTriples = pBezTriple.fetchData(dataRepository.getInputStream()); |
||||
bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); |
||||
} |
||||
|
||||
Ipo ipo = new Ipo(bezierCurves); |
||||
tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); |
||||
} |
||||
} |
||||
return tracks.toArray(new BoneTrack[tracks.size()]); |
||||
} |
||||
|
||||
/** |
||||
* This method parses the information stored inside the curve rna path and returns the proper type |
||||
* of the curve. |
||||
* @param rnaPath the curve's rna path |
||||
* @param arrayIndex the array index of the stored data |
||||
* @return the type of the curve |
||||
*/ |
||||
protected int getCurveType(String rnaPath, int arrayIndex) { |
||||
if (rnaPath.endsWith(".location")) { |
||||
return Ipo.AC_LOC_X + arrayIndex; |
||||
} |
||||
if (rnaPath.endsWith(".rotation_quaternion")) { |
||||
return Ipo.AC_QUAT_W + arrayIndex; |
||||
} |
||||
if (rnaPath.endsWith(".scale")) { |
||||
return Ipo.AC_SIZE_X + arrayIndex; |
||||
} |
||||
throw new IllegalStateException("Unknown curve rna path: " + rnaPath); |
||||
} |
||||
} |
@ -1,52 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
|
||||
/** |
||||
* A class that is used in light calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class CameraHelper extends com.jme3.scene.plugins.blender.helpers.v249.CameraHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public CameraHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
@Override |
||||
public Camera toCamera(Structure structure) throws BlenderFileException { |
||||
if (blenderVersion < 250) { |
||||
return super.toCamera(structure); |
||||
} |
||||
Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); |
||||
int type = ((Number) structure.getFieldValue("type")).intValue(); |
||||
if (type != 0 && type != 1) { |
||||
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); |
||||
type = 0; |
||||
} |
||||
//type==0 - perspective; type==1 - orthographic; perspective is used as default
|
||||
result.setParallelProjection(type == 1); |
||||
float aspect = 0; |
||||
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); |
||||
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); |
||||
if (type == 0) { |
||||
aspect = ((Number) structure.getFieldValue("lens")).floatValue(); |
||||
} else { |
||||
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); |
||||
} |
||||
result.setFrustumPerspective(45, aspect, clipsta, clipend); |
||||
return result; |
||||
} |
||||
} |
@ -1,38 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
|
||||
/** |
||||
* This class should be used for constraint calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ConstraintHelper extends com.jme3.scene.plugins.blender.helpers.v249.ConstraintHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName()); |
||||
|
||||
/** |
||||
* Helper constructor. It's main task is to generate the affection functions. These functions are common to all |
||||
* ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall |
||||
* consider refactoring. The constructor parses the given blender version and stores the result. Some |
||||
* functionalities may differ in different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { |
||||
super(blenderVersion, dataRepository); |
||||
} |
||||
|
||||
@Override |
||||
public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
if (blenderVersion < 250) { |
||||
super.loadConstraints(objectStructure, dataRepository); |
||||
} else { |
||||
LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !"); |
||||
//TODO: to implement
|
||||
} |
||||
} |
||||
} |
@ -1,18 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* A class that is used in mesh calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class CurvesHelper extends com.jme3.scene.plugins.blender.helpers.v249.CurvesHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public CurvesHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,19 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* This class helps to compute values from interpolation curves for features like animation or constraint influence. The |
||||
* curves are 3rd degree bezier curves. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class IpoHelper extends com.jme3.scene.plugins.blender.helpers.v249.IpoHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public IpoHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,49 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* A class that is used in light calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class LightHelper extends com.jme3.scene.plugins.blender.helpers.v249.LightHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public LightHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,45 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
public class MaterialHelper extends com.jme3.scene.plugins.blender.helpers.v249.MaterialHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public MaterialHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,49 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* A class that is used in mesh calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class MeshHelper extends com.jme3.scene.plugins.blender.helpers.v249.MeshHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public MeshHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,49 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* A class that is used in modifiers calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ModifierHelper extends com.jme3.scene.plugins.blender.helpers.v249.ModifierHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ModifierHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,52 +0,0 @@ |
||||
/* |
||||
* |
||||
* $Id: noise.c 14611 2008-04-29 08:24:33Z campbellbarton $ |
||||
* |
||||
* ***** BEGIN GPL LICENSE BLOCK ***** |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License |
||||
* as published by the Free Software Foundation; either version 2 |
||||
* of the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software Foundation, |
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||||
* |
||||
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. |
||||
* All rights reserved. |
||||
* |
||||
* The Original Code is: all of this file. |
||||
* |
||||
* Contributor(s): none yet. |
||||
* |
||||
* ***** END GPL LICENSE BLOCK ***** |
||||
* |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* Methods of this class are copied from blender 2.49 source code and modified so that they can be used in java. They are mostly NOT |
||||
* documented because they are not documented in blender's source code. If I find a proper description or discover what they actually do and |
||||
* what parameters mean - I shall describe such methods :) If anyone have some hint what these methods are doing please rite the proper |
||||
* javadoc documentation. These methods should be used to create generated textures. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
public class NoiseHelper extends com.jme3.scene.plugins.blender.helpers.v249.NoiseHelper { |
||||
|
||||
/** |
||||
* Constructor. Stores the blender version number and loads the constants needed for computations. |
||||
* |
||||
* @param blenderVersion |
||||
* the number of blender version |
||||
*/ |
||||
public NoiseHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,49 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* A class that is used in object calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ObjectHelper extends com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ObjectHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,18 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
/** |
||||
* This class helps to import the special effects from blender file. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
public class ParticlesHelper extends com.jme3.scene.plugins.blender.helpers.v249.ParticlesHelper { |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ParticlesHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
} |
@ -1,84 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers; |
||||
|
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.texture.Texture; |
||||
|
||||
/** |
||||
* A class that is used in texture calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class TextureHelper extends com.jme3.scene.plugins.blender.helpers.v249.TextureHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName()); |
||||
public static final int TEX_POINTDENSITY = 14; |
||||
public static final int TEX_VOXELDATA = 15; |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public TextureHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
@Override |
||||
public Texture getTexture(Structure tex, DataRepository dataRepository) throws BlenderFileException { |
||||
if (blenderVersion < 250) { |
||||
return super.getTexture(tex, dataRepository); |
||||
} |
||||
Texture result = (Texture) dataRepository.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); |
||||
if (result != null) { |
||||
return result; |
||||
} |
||||
int type = ((Number) tex.getFieldValue("type")).intValue(); |
||||
switch (type) { |
||||
case TEX_POINTDENSITY: |
||||
LOGGER.warning("Point density texture loading currently not supported!"); |
||||
break; |
||||
case TEX_VOXELDATA: |
||||
LOGGER.warning("Voxel data texture loading currently not supported!"); |
||||
break; |
||||
default: |
||||
result = super.getTexture(tex, dataRepository); |
||||
} |
||||
return result; |
||||
} |
||||
} |
@ -1,65 +0,0 @@ |
||||
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. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class CameraHelper extends AbstractBlenderHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName()); |
||||
protected static final int DEFAULT_CAM_WIDTH = 100; |
||||
protected static final int DEFAULT_CAM_HEIGHT = 100; |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public CameraHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
/** |
||||
* This method reads the camera object. |
||||
* @param structure the structure containing the camera data |
||||
* @return the camera object |
||||
* @throws BlenderFileException |
||||
*/ |
||||
public Camera toCamera(Structure structure) throws BlenderFileException { |
||||
Camera result = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); |
||||
int type = ((Number) structure.getFieldValue("type")).intValue(); |
||||
if (type != 0 && type != 1) { |
||||
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type); |
||||
type = 0; |
||||
} |
||||
//type==0 - perspective; type==1 - orthographic; perspective is used as default
|
||||
result.setParallelProjection(type == 1); |
||||
float angle = ((Number) structure.getFieldValue("angle")).floatValue(); |
||||
float aspect = 0; |
||||
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue(); |
||||
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue(); |
||||
if (type == 0) { |
||||
aspect = ((Number) structure.getFieldValue("lens")).floatValue(); |
||||
} else { |
||||
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); |
||||
} |
||||
result.setFrustumPerspective(angle, aspect, clipsta, clipend); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) { |
||||
return (dataRepository.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.CAMERAS) != 0; |
||||
} |
||||
} |
@ -1,756 +0,0 @@ |
||||
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; |
||||
import java.util.logging.Level; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
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; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
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; |
||||
import com.jme3.scene.plugins.blender.structures.Ipo; |
||||
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
|
||||
/** |
||||
* This class should be used for constraint calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ConstraintHelper extends AbstractBlenderHelper { |
||||
|
||||
/** |
||||
* A table containing implementations of influence functions for constraints. It should contain functions for |
||||
* blender at least 249 and higher. |
||||
*/ |
||||
protected static AbstractInfluenceFunction[] influenceFunctions; |
||||
/** |
||||
* Constraints stored for object with the given old memory address. |
||||
*/ |
||||
protected Map<Long, Constraint[]> constraints = new HashMap<Long, Constraint[]>(); |
||||
|
||||
/** |
||||
* Helper constructor. It's main task is to generate the affection functions. These functions are common to all |
||||
* ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall |
||||
* consider refactoring. The constructor parses the given blender version and stores the result. Some |
||||
* functionalities may differ in different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ConstraintHelper(String blenderVersion, DataRepository dataRepository) { |
||||
super(blenderVersion); |
||||
this.initializeConstraintFunctions(dataRepository); |
||||
} |
||||
|
||||
/** |
||||
* This method initializes constraint functions for Blender 2.49. |
||||
* @param dataRepository |
||||
* the data repository |
||||
*/ |
||||
private synchronized void initializeConstraintFunctions(DataRepository dataRepository) { |
||||
if (influenceFunctions == null) { |
||||
influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1]; |
||||
//ACTION constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) { |
||||
}; |
||||
|
||||
//CHILDOF constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) { |
||||
}; |
||||
|
||||
//CLAMPTO constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
this.validateConstraintType(constraint.getData()); |
||||
LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented
|
||||
} |
||||
}; |
||||
|
||||
//DISTLIMIT constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintStructure = constraint.getData(); |
||||
this.validateConstraintType(constraintStructure); |
||||
Vector3f targetLocation = this.getTargetLocation(constraint); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
//TODO: target vertex group !!!
|
||||
float dist = ((Number) constraintStructure.getFieldValue("dist")).floatValue(); |
||||
int mode = ((Number) constraintStructure.getFieldValue("mode")).intValue(); |
||||
|
||||
int maxFrames = boneTrack.getTimes().length; |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f v = translations[frame].subtract(targetLocation); |
||||
float currentDistance = v.length(); |
||||
float influence = constraint.getIpo().calculateValue(frame); |
||||
float modifier = 0.0f; |
||||
switch (mode) { |
||||
case LIMITDIST_INSIDE: |
||||
if (currentDistance >= dist) { |
||||
modifier = (dist - currentDistance) / currentDistance; |
||||
} |
||||
break; |
||||
case LIMITDIST_ONSURFACE: |
||||
modifier = (dist - currentDistance) / currentDistance; |
||||
break; |
||||
case LIMITDIST_OUTSIDE: |
||||
if (currentDistance <= dist) { |
||||
modifier = (dist - currentDistance) / currentDistance; |
||||
} |
||||
break; |
||||
default: |
||||
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode); |
||||
} |
||||
translations[frame].addLocal(v.multLocal(modifier * influence)); |
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//FOLLOWPATH constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
this.validateConstraintType(constraint.getData()); |
||||
LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented
|
||||
} |
||||
}; |
||||
|
||||
//KINEMATIC constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
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, 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(); |
||||
|
||||
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); |
||||
} |
||||
|
||||
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("===============================");
|
||||
// }
|
||||
} catch(BlenderFileException e) { |
||||
LOGGER.severe(e.getLocalizedMessage()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns bones used for rotation calculations. |
||||
* @param bone |
||||
* the bone to which the constraint is applied |
||||
* @param skeleton |
||||
* the skeleton owning the bone and its ancestors |
||||
* @param boneAnimation |
||||
* the bone animation data that stores the traces for the skeleton's bones |
||||
* @return a list of bones to imitate the bone's movement during IK solving |
||||
*/ |
||||
private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
List<CalculationBone> bonesList = new ArrayList<CalculationBone>(); |
||||
Bone currentBone = bone; |
||||
do { |
||||
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
|
||||
CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]); |
||||
for (int i = result.length - 1; i > 0; --i) { |
||||
result[i].attachChild(result[i - 1]); |
||||
} |
||||
return result; |
||||
} |
||||
}; |
||||
|
||||
//LOCKTRACK constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) { |
||||
}; |
||||
|
||||
//LOCLIKE constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintData = constraint.getData(); |
||||
this.validateConstraintType(constraintData); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
Vector3f targetLocation = this.getTargetLocation(constraint); |
||||
int flag = ((Number) constraintData.getFieldValue("flag")).intValue(); |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
int maxFrames = translations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f offset = Vector3f.ZERO; |
||||
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
|
||||
offset = translations[frame].clone(); |
||||
} |
||||
|
||||
if ((flag & LOCLIKE_X) != 0) { |
||||
translations[frame].x = targetLocation.x; |
||||
if ((flag & LOCLIKE_X_INVERT) != 0) { |
||||
translations[frame].x = -translations[frame].x; |
||||
} |
||||
} else if ((flag & LOCLIKE_Y) != 0) { |
||||
translations[frame].y = targetLocation.y; |
||||
if ((flag & LOCLIKE_Y_INVERT) != 0) { |
||||
translations[frame].y = -translations[frame].y; |
||||
} |
||||
} else if ((flag & LOCLIKE_Z) != 0) { |
||||
translations[frame].z = targetLocation.z; |
||||
if ((flag & LOCLIKE_Z_INVERT) != 0) { |
||||
translations[frame].z = -translations[frame].z; |
||||
} |
||||
} |
||||
translations[frame].addLocal(offset);//TODO: ipo influence
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//LOCLIMIT constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintStructure = constraint.getData(); |
||||
this.validateConstraintType(constraintStructure); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue(); |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
int maxFrames = translations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float influence = constraint.getIpo().calculateValue(frame); |
||||
if ((flag & LIMIT_XMIN) != 0) { |
||||
float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue(); |
||||
if (translations[frame].x < xmin) { |
||||
translations[frame].x -= (translations[frame].x - xmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_XMAX) != 0) { |
||||
float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue(); |
||||
if (translations[frame].x > xmax) { |
||||
translations[frame].x -= (translations[frame].x - xmax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMIN) != 0) { |
||||
float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue(); |
||||
if (translations[frame].y < ymin) { |
||||
translations[frame].y -= (translations[frame].y - ymin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMAX) != 0) { |
||||
float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue(); |
||||
if (translations[frame].y > ymax) { |
||||
translations[frame].y -= (translations[frame].y - ymax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMIN) != 0) { |
||||
float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue(); |
||||
if (translations[frame].z < zmin) { |
||||
translations[frame].z -= (translations[frame].z - zmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMAX) != 0) { |
||||
float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue(); |
||||
if (translations[frame].z > zmax) { |
||||
translations[frame].z -= (translations[frame].z - zmax) * influence; |
||||
} |
||||
}//TODO: consider constraint space !!!
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//MINMAX constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) { |
||||
}; |
||||
|
||||
//NULL constraint - does nothing
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) { |
||||
}; |
||||
|
||||
//PYTHON constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) { |
||||
}; |
||||
|
||||
//RIGIDBODYJOINT constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) { |
||||
}; |
||||
|
||||
//ROTLIKE constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) { |
||||
|
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintData = constraint.getData(); |
||||
this.validateConstraintType(constraintData); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
Quaternion targetRotation = this.getTargetRotation(constraint); |
||||
int flag = ((Number) constraintData.getFieldValue("flag")).intValue(); |
||||
float[] targetAngles = targetRotation.toAngles(null); |
||||
Quaternion[] rotations = boneTrack.getRotations(); |
||||
int maxFrames = rotations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float[] angles = rotations[frame].toAngles(null); |
||||
|
||||
Quaternion offset = Quaternion.IDENTITY; |
||||
if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
|
||||
offset = rotations[frame].clone(); |
||||
} |
||||
|
||||
if ((flag & ROTLIKE_X) != 0) { |
||||
angles[0] = targetAngles[0]; |
||||
if ((flag & ROTLIKE_X_INVERT) != 0) { |
||||
angles[0] = -angles[0]; |
||||
} |
||||
} else if ((flag & ROTLIKE_Y) != 0) { |
||||
angles[1] = targetAngles[1]; |
||||
if ((flag & ROTLIKE_Y_INVERT) != 0) { |
||||
angles[1] = -angles[1]; |
||||
} |
||||
} else if ((flag & ROTLIKE_Z) != 0) { |
||||
angles[2] = targetAngles[2]; |
||||
if ((flag & ROTLIKE_Z_INVERT) != 0) { |
||||
angles[2] = -angles[2]; |
||||
} |
||||
} |
||||
rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//ROTLIMIT constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) { |
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintStructure = constraint.getData(); |
||||
this.validateConstraintType(constraintStructure); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue(); |
||||
Quaternion[] rotations = boneTrack.getRotations(); |
||||
int maxFrames = rotations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float[] angles = rotations[frame].toAngles(null); |
||||
float influence = constraint.getIpo().calculateValue(frame); |
||||
if ((flag & LIMIT_XROT) != 0) { |
||||
float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float difference = 0.0f; |
||||
if (angles[0] < xmin) { |
||||
difference = (angles[0] - xmin) * influence; |
||||
} else if (angles[0] > xmax) { |
||||
difference = (angles[0] - xmax) * influence; |
||||
} |
||||
angles[0] -= difference; |
||||
} |
||||
if ((flag & LIMIT_YROT) != 0) { |
||||
float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float difference = 0.0f; |
||||
if (angles[1] < ymin) { |
||||
difference = (angles[1] - ymin) * influence; |
||||
} else if (angles[1] > ymax) { |
||||
difference = (angles[1] - ymax) * influence; |
||||
} |
||||
angles[1] -= difference; |
||||
} |
||||
if ((flag & LIMIT_ZROT) != 0) { |
||||
float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD; |
||||
float difference = 0.0f; |
||||
if (angles[2] < zmin) { |
||||
difference = (angles[2] - zmin) * influence; |
||||
} else if (angles[2] > zmax) { |
||||
difference = (angles[2] - zmax) * influence; |
||||
} |
||||
angles[2] -= difference; |
||||
} |
||||
rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//SHRINKWRAP constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) { |
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintStructure = constraint.getData(); |
||||
this.validateConstraintType(constraintStructure); |
||||
|
||||
//loading mesh points (blender ensures that the target is a mesh-object)
|
||||
List<Vector3f> pts = new ArrayList<Vector3f>(); |
||||
try { |
||||
Node node = (Node)this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE); |
||||
for(Spatial spatial : node.getChildren()) { |
||||
if(spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position); |
||||
for(int i=0;i<floatBuffer.limit();i+=3) { |
||||
pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//modifying traces
|
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
Vector3f[] translations = boneTrack.getTranslations(); |
||||
Quaternion[] rotations = boneTrack.getRotations(); |
||||
int maxFrames = translations.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f currentTranslation = translations[frame]; |
||||
|
||||
//looking for minimum distanced point
|
||||
Vector3f minDistancePoint = null; |
||||
float distance = Float.MAX_VALUE; |
||||
for(Vector3f p : pts) { |
||||
float temp = currentTranslation.distance(p); |
||||
if(temp < distance) { |
||||
distance = temp; |
||||
minDistancePoint = p; |
||||
} |
||||
} |
||||
translations[frame] = minDistancePoint.clone(); |
||||
} |
||||
|
||||
boneTrack.setKeyframes(boneTrack.getTimes(), translations, rotations, boneTrack.getScales()); |
||||
} |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.severe(e.getLocalizedMessage()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//SIZELIKE constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) { |
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintData = constraint.getData(); |
||||
this.validateConstraintType(constraintData); |
||||
Vector3f targetScale = this.getTargetLocation(constraint); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) constraintData.getFieldValue("flag")).intValue(); |
||||
Vector3f[] scales = boneTrack.getScales(); |
||||
int maxFrames = scales.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
Vector3f offset = Vector3f.ZERO; |
||||
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
|
||||
offset = scales[frame].clone(); |
||||
} |
||||
|
||||
if ((flag & SIZELIKE_X) != 0) { |
||||
scales[frame].x = targetScale.x; |
||||
} else if ((flag & SIZELIKE_Y) != 0) { |
||||
scales[frame].y = targetScale.y; |
||||
} else if ((flag & SIZELIKE_Z) != 0) { |
||||
scales[frame].z = targetScale.z; |
||||
} |
||||
scales[frame].addLocal(offset);//TODO: ipo influence
|
||||
//TODO: add or multiply???
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//SIZELIMIT constraint
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) { |
||||
@Override |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Structure constraintStructure = constraint.getData(); |
||||
this.validateConstraintType(constraintStructure); |
||||
BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint); |
||||
if (boneTrack != null) { |
||||
int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue(); |
||||
Vector3f[] scales = boneTrack.getScales(); |
||||
int maxFrames = scales.length; |
||||
for (int frame = 0; frame < maxFrames; ++frame) { |
||||
float influence = constraint.getIpo().calculateValue(frame); |
||||
if ((flag & LIMIT_XMIN) != 0) { |
||||
float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue(); |
||||
if (scales[frame].x < xmin) { |
||||
scales[frame].x -= (scales[frame].x - xmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_XMAX) != 0) { |
||||
float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue(); |
||||
if (scales[frame].x > xmax) { |
||||
scales[frame].x -= (scales[frame].x - xmax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMIN) != 0) { |
||||
float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue(); |
||||
if (scales[frame].y < ymin) { |
||||
scales[frame].y -= (scales[frame].y - ymin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_YMAX) != 0) { |
||||
float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue(); |
||||
if (scales[frame].y > ymax) { |
||||
scales[frame].y -= (scales[frame].y - ymax) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMIN) != 0) { |
||||
float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue(); |
||||
if (scales[frame].z < zmin) { |
||||
scales[frame].z -= (scales[frame].z - zmin) * influence; |
||||
} |
||||
} |
||||
if ((flag & LIMIT_ZMAX) != 0) { |
||||
float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue(); |
||||
if (scales[frame].z > zmax) { |
||||
scales[frame].z -= (scales[frame].z - zmax) * influence; |
||||
} |
||||
}//TODO: consider constraint space !!!
|
||||
} |
||||
boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//STRETCHTO constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) { |
||||
}; |
||||
|
||||
//TRANSFORM constraint (TODO: to implement)
|
||||
influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) { |
||||
}; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method reads constraints for for the given structure. The constraints are loaded only once for object/bone. |
||||
* @param ownerOMA |
||||
* the owner's old memory address |
||||
* @param objectStructure |
||||
* the structure we read constraint's for |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
*/ |
||||
public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
// reading influence ipos for the constraints
|
||||
IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); |
||||
Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>(); |
||||
Pointer pActions = (Pointer) objectStructure.getFieldValue("action"); |
||||
if (pActions.isNotNull()) { |
||||
List<Structure> actions = pActions.fetchData(dataRepository.getInputStream()); |
||||
for (Structure action : actions) { |
||||
Structure chanbase = (Structure) action.getFieldValue("chanbase"); |
||||
List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository); |
||||
for (Structure actionChannel : actionChannels) { |
||||
Map<String, Ipo> ipos = new HashMap<String, Ipo>(); |
||||
Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels"); |
||||
List<Structure> constraintChannels = constChannels.evaluateListBase(dataRepository); |
||||
for (Structure constraintChannel : constraintChannels) { |
||||
Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo"); |
||||
if (pIpo.isNotNull()) { |
||||
String constraintName = constraintChannel.getFieldValue("name").toString(); |
||||
Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository); |
||||
ipos.put(constraintName, ipo); |
||||
} |
||||
} |
||||
String actionName = actionChannel.getFieldValue("name").toString(); |
||||
constraintsIpos.put(actionName, ipos); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//loading constraints connected with the object's bones
|
||||
List<Constraint> constraintsList = new ArrayList<Constraint>(); |
||||
Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
|
||||
if (pPose.isNotNull()) { |
||||
//getting pose channels
|
||||
List<Structure> poseChannels = ((Structure) pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository); |
||||
for (Structure poseChannel : poseChannels) { |
||||
Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress()); |
||||
//the name is read directly from structure because bone might not yet be loaded
|
||||
String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString(); |
||||
List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository); |
||||
for (Structure constraint : constraints) { |
||||
int type = ((Number) constraint.getFieldValue("type")).intValue(); |
||||
String constraintName = constraint.getFieldValue("name").toString(); |
||||
Map<String, Ipo> ipoMap = constraintsIpos.get(name); |
||||
Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName); |
||||
if (ipo == null) { |
||||
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); |
||||
ipo = ipoHelper.createIpo(enforce); |
||||
} |
||||
Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue()); |
||||
Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue()); |
||||
Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository); |
||||
constraintsList.add(c); |
||||
} |
||||
} |
||||
} |
||||
// TODO: reading constraints for objects (implement when object's animation will be available)
|
||||
List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository); |
||||
for(Structure constraintChannel : constraintChannels) { |
||||
System.out.println(constraintChannel); |
||||
} |
||||
|
||||
//loading constraints connected with the object itself (TODO: test this)
|
||||
if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) { |
||||
List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository); |
||||
Constraint[] result = new Constraint[constraints.size()]; |
||||
int i = 0; |
||||
|
||||
for(Structure constraint : constraints) { |
||||
int type = ((Number)constraint.getFieldValue("type")).intValue(); |
||||
String constraintName = constraint.getFieldValue("name").toString(); |
||||
String objectName = objectStructure.getName(); |
||||
|
||||
Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName); |
||||
Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null; |
||||
if (ipo == null) { |
||||
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); |
||||
ipo = ipoHelper.createIpo(enforce); |
||||
} |
||||
Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue()); |
||||
Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue()); |
||||
result[i++] = new Constraint(constraint, influenceFunctions[type], null, |
||||
ownerSpace, targetSpace, ipo, dataRepository);//TODO: influence ipos for object animation
|
||||
} |
||||
this.constraints.put(objectStructure.getOldMemoryAddress(), result); |
||||
} |
||||
|
||||
if (constraintsList.size() > 0) { |
||||
this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()])); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns a list of constraints of the feature's constraints. The order of constraints is important. |
||||
* @param ownerOMA |
||||
* the owner's old memory address |
||||
* @return a table of constraints for the feature specified by old memory address |
||||
*/ |
||||
public Constraint[] getConstraints(Long ownerOMA) { |
||||
return constraints.get(ownerOMA); |
||||
} |
||||
|
||||
@Override |
||||
public void clearState() { |
||||
constraints.clear(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) { |
||||
return true; |
||||
} |
||||
} |
@ -1,762 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.helpers.v249; |
||||
|
||||
import java.nio.FloatBuffer; |
||||
import java.nio.IntBuffer; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Map.Entry; |
||||
import java.util.Set; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.AnimControl; |
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.animation.SkeletonControl; |
||||
import com.jme3.bounding.BoundingBox; |
||||
import com.jme3.bounding.BoundingSphere; |
||||
import com.jme3.bounding.BoundingVolume; |
||||
import com.jme3.effect.ParticleEmitter; |
||||
import com.jme3.effect.shapes.EmitterMeshVertexShape; |
||||
import com.jme3.effect.shapes.EmitterShape; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.math.Transform; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.VertexBuffer.Type; |
||||
import com.jme3.scene.plugins.blender.data.FileBlockHeader; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.helpers.ParticlesHelper; |
||||
import com.jme3.scene.plugins.blender.structures.Constraint; |
||||
import com.jme3.scene.plugins.blender.structures.Ipo; |
||||
import com.jme3.scene.plugins.blender.structures.Modifier; |
||||
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.utils.DynamicArray; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
import com.jme3.scene.plugins.ogre.AnimData; |
||||
import com.jme3.scene.shape.Curve; |
||||
|
||||
/** |
||||
* A class that is used in modifiers calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ModifierHelper extends AbstractBlenderHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName()); |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ModifierHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
/** |
||||
* This method applies modifier to the object. |
||||
* @param node |
||||
* the loaded object |
||||
* @param modifier |
||||
* the modifier to apply |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return the node to whom the modifier was applied |
||||
*/ |
||||
public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) { |
||||
if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) { |
||||
return this.applyArmatureModifierData(node, modifier, dataRepository); |
||||
} else if (Modifier.OBJECT_ANIMATION_MODIFIER_DATA.equals(modifier.getType())) { |
||||
return this.applyObjectAnimationModifier(node, modifier, dataRepository); |
||||
} else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) { |
||||
return this.applyArrayModifierData(node, modifier, dataRepository); |
||||
} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) { |
||||
return this.applyParticleSystemModifierData(node, modifier, dataRepository); |
||||
} else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) { |
||||
return this.applyMirrorModifierData(node, modifier, dataRepository); |
||||
} else { |
||||
LOGGER.log(Level.WARNING, "Modifier: {0} not yet implemented!!!", modifier.getType()); |
||||
return node; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method reads the given object's modifiers. |
||||
* @param objectStructure |
||||
* the object structure |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @param converter |
||||
* the converter object (in some cases we need to read an object first before loading the modifier) |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow corrupted |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers"); |
||||
List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository); |
||||
for (Structure modifier : modifiers) { |
||||
Object loadedModifier = null; |
||||
Object modifierAdditionalData = null; |
||||
if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {// ****************ARRAY MODIFIER
|
||||
Map<String, Object> params = new HashMap<String, Object>(); |
||||
|
||||
Number fittype = (Number) modifier.getFieldValue("fit_type"); |
||||
params.put("fittype", fittype); |
||||
switch (fittype.intValue()) { |
||||
case 0:// FIXED COUNT
|
||||
params.put("count", modifier.getFieldValue("count")); |
||||
break; |
||||
case 1:// FIXED LENGTH
|
||||
params.put("length", modifier.getFieldValue("length")); |
||||
break; |
||||
case 2:// FITCURVE
|
||||
Pointer pCurveOb = (Pointer) modifier.getFieldValue("curve_ob"); |
||||
float length = 0; |
||||
if (pCurveOb.isNotNull()) { |
||||
Structure curveStructure = pCurveOb.fetchData(dataRepository.getInputStream()).get(0); |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Node curveObject = (Node) objectHelper.toObject(curveStructure, dataRepository); |
||||
Set<Number> referencesToCurveLengths = new HashSet<Number>(curveObject.getChildren().size()); |
||||
for (Spatial spatial : curveObject.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
if (mesh instanceof Curve) { |
||||
length += ((Curve) mesh).getLength(); |
||||
} else { |
||||
//if bevel object has several parts then each mesh will have the same reference
|
||||
//to length value (and we should use only one)
|
||||
Number curveLength = spatial.getUserData("curveLength"); |
||||
if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) { |
||||
length += curveLength.floatValue(); |
||||
referencesToCurveLengths.add(curveLength); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
params.put("length", Float.valueOf(length)); |
||||
params.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH
|
||||
break; |
||||
default: |
||||
assert false : "Unknown array modifier fit type: " + fittype; |
||||
} |
||||
|
||||
// offset parameters
|
||||
int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue(); |
||||
if ((offsettype & 0x01) != 0) {// Constant offset
|
||||
DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifier.getFieldValue("offset"); |
||||
float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()}; |
||||
params.put("offset", offset); |
||||
} |
||||
if ((offsettype & 0x02) != 0) {// Relative offset
|
||||
DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifier.getFieldValue("scale"); |
||||
float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()}; |
||||
params.put("scale", scale); |
||||
} |
||||
if ((offsettype & 0x04) != 0) {// Object offset
|
||||
Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob"); |
||||
if (pOffsetObject.isNotNull()) { |
||||
params.put("offsetob", pOffsetObject); |
||||
} |
||||
} |
||||
|
||||
// start cap and end cap
|
||||
Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap"); |
||||
if (pStartCap.isNotNull()) { |
||||
params.put("startcap", pStartCap); |
||||
} |
||||
Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap"); |
||||
if (pEndCap.isNotNull()) { |
||||
params.put("endcap", pEndCap); |
||||
} |
||||
loadedModifier = params; |
||||
} else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) {// ****************MIRROR MODIFIER
|
||||
Map<String, Object> params = new HashMap<String, Object>(); |
||||
|
||||
params.put("flag", modifier.getFieldValue("flag")); |
||||
params.put("tolerance", modifier.getFieldValue("tolerance")); |
||||
Pointer pMirrorOb = (Pointer) modifier.getFieldValue("mirror_ob"); |
||||
if (pMirrorOb.isNotNull()) { |
||||
params.put("mirrorob", pMirrorOb); |
||||
} |
||||
loadedModifier = params; |
||||
} else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {// ****************ARMATURE MODIFIER
|
||||
Pointer pArmatureObject = (Pointer) modifier.getFieldValue("object"); |
||||
if (pArmatureObject.isNotNull()) { |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE); |
||||
if (armatureObject == null) {// we check this first not to fetch the structure unnecessary
|
||||
armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0); |
||||
objectHelper.toObject(armatureObject, dataRepository); |
||||
} |
||||
modifierAdditionalData = armatureObject.getOldMemoryAddress(); |
||||
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); |
||||
|
||||
// changing bones matrices so that they fit the current object (that is why we need a copy of a skeleton)
|
||||
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); |
||||
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); |
||||
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); |
||||
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); |
||||
|
||||
//setting the bones structure inside the skeleton (thus completing its loading)
|
||||
Skeleton skeleton = new Skeleton(bones); |
||||
dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton); |
||||
|
||||
String objectName = objectStructure.getName(); |
||||
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); |
||||
if (animationNames != null && animationNames.size() > 0) { |
||||
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>(); |
||||
List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); |
||||
for (FileBlockHeader header : actionHeaders) { |
||||
Structure actionStructure = header.getStructure(dataRepository); |
||||
String actionName = actionStructure.getName(); |
||||
if (animationNames.contains(actionName)) { |
||||
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName); |
||||
int fps = dataRepository.getBlenderKey().getFps(); |
||||
float start = (float) animationFrames[0] / (float) fps; |
||||
float stop = (float) animationFrames[1] / (float) fps; |
||||
BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start); |
||||
boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName)); |
||||
animations.add(boneAnimation); |
||||
} |
||||
} |
||||
loadedModifier = new AnimData(new Skeleton(bones), animations); |
||||
} |
||||
} else { |
||||
LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifier.getType()); |
||||
} |
||||
} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {// ****************PARTICLES MODIFIER
|
||||
Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys"); |
||||
if (pParticleSystem.isNotNull()) { |
||||
ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class); |
||||
Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0); |
||||
loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository); |
||||
} |
||||
} |
||||
// adding modifier to the modifier's lists
|
||||
if (loadedModifier != null) { |
||||
dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData); |
||||
modifierAdditionalData = null; |
||||
} |
||||
} |
||||
|
||||
//at the end read object's animation modifier
|
||||
Modifier objectAnimationModifier = this.readObjectAnimation(objectStructure, dataRepository); |
||||
if (objectAnimationModifier != null) { |
||||
dataRepository.addModifier(objectStructure.getOldMemoryAddress(), |
||||
objectAnimationModifier.getType(), |
||||
objectAnimationModifier.getJmeModifierRepresentation(), |
||||
objectAnimationModifier.getAdditionalData()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method reads animation of the object itself (without bones) and stores it as an ArmatureModifierData |
||||
* modifier. The animation is returned as a modifier. It should be later applied regardless other modifiers. The |
||||
* reason for this is that object may not have modifiers added but it's animation should be working. |
||||
* @param objectStructure |
||||
* the structure of the object |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return animation modifier is returned, it should be separately applied when the object is loaded |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow corrupted |
||||
*/ |
||||
protected Modifier readObjectAnimation(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo"); |
||||
if (pIpo.isNotNull()) { |
||||
//check if there is an action name connected with this ipo
|
||||
String objectAnimationName = null; |
||||
List<FileBlockHeader> actionBlocks = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); |
||||
for (FileBlockHeader actionBlock : actionBlocks) { |
||||
Structure action = actionBlock.getStructure(dataRepository); |
||||
List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(dataRepository); |
||||
if (actionChannels.size() == 1) {//object's animtion action has only one channel
|
||||
Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo"); |
||||
if (pChannelIpo.equals(pIpo)) { |
||||
objectAnimationName = action.getName(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
String objectName = objectStructure.getName(); |
||||
if (objectAnimationName == null) {//set the object's animation name to object's name
|
||||
objectAnimationName = objectName; |
||||
} |
||||
|
||||
IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); |
||||
Structure ipoStructure = pIpo.fetchData(dataRepository.getInputStream()).get(0); |
||||
Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); |
||||
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, objectAnimationName); |
||||
if (animationFrames == null) {//if the name was created here there are no frames set for the animation
|
||||
animationFrames = new int[]{1, ipo.getLastFrame()}; |
||||
} |
||||
int fps = dataRepository.getBlenderKey().getFps(); |
||||
float start = (float) animationFrames[0] / (float) fps; |
||||
float stop = (float) animationFrames[1] / (float) fps; |
||||
|
||||
//calculating track for the only bone in this skeleton
|
||||
BoneTrack[] tracks = new BoneTrack[1]; |
||||
tracks[0] = ipo.calculateTrack(0, animationFrames[0], animationFrames[1], fps); |
||||
|
||||
BoneAnimation boneAnimation = new BoneAnimation(objectAnimationName, stop - start); |
||||
boneAnimation.setTracks(tracks); |
||||
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>(1); |
||||
animations.add(boneAnimation); |
||||
|
||||
//preparing the object's bone
|
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Transform t = objectHelper.getTransformation(objectStructure, dataRepository); |
||||
Bone bone = new Bone(null); |
||||
bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale()); |
||||
|
||||
return new Modifier(Modifier.OBJECT_ANIMATION_MODIFIER_DATA, new AnimData(new Skeleton(new Bone[]{bone}), animations), objectStructure.getOldMemoryAddress()); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* This method applies particles emitter to the given node. |
||||
* @param node |
||||
* the particles emitter node |
||||
* @param modifier |
||||
* the modifier containing the emitter data |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return node with particles' emitter applied |
||||
*/ |
||||
protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) { |
||||
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class); |
||||
ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation(); |
||||
emitter = emitter.clone(); |
||||
|
||||
// veryfying the alpha function for particles' texture
|
||||
Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE; |
||||
char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1); |
||||
if (nameSuffix == 'B' || nameSuffix == 'N') { |
||||
alphaFunction = MaterialHelper.ALPHA_MASK_NONE; |
||||
} |
||||
// removing the type suffix from the name
|
||||
emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1)); |
||||
|
||||
// applying emitter shape
|
||||
EmitterShape emitterShape = emitter.getShape(); |
||||
List<Mesh> meshes = new ArrayList<Mesh>(); |
||||
for (Spatial spatial : node.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
if (mesh != null) { |
||||
meshes.add(mesh); |
||||
Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository); |
||||
emitter.setMaterial(material);// TODO: divide into several pieces
|
||||
} |
||||
} |
||||
} |
||||
if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) { |
||||
((EmitterMeshVertexShape) emitterShape).setMeshes(meshes); |
||||
} |
||||
|
||||
node.attachChild(emitter); |
||||
return node; |
||||
} |
||||
|
||||
/** |
||||
* This method applies ArmatureModifierData to the loaded object. |
||||
* @param node |
||||
* the loaded object |
||||
* @param modifier |
||||
* the modifier to apply |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return the node to whom the modifier was applied |
||||
*/ |
||||
protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) { |
||||
AnimData ad = (AnimData) modifier.getJmeModifierRepresentation(); |
||||
ArrayList<BoneAnimation> animList = ad.anims; |
||||
Long modifierArmatureObject = (Long) modifier.getAdditionalData(); |
||||
if (animList != null && animList.size() > 0) { |
||||
ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class); |
||||
Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject); |
||||
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>(); |
||||
for (int i = 0; i < animList.size(); ++i) { |
||||
BoneAnimation boneAnimation = animList.get(i).clone(); |
||||
|
||||
// baking constraints into animations
|
||||
if (constraints != null && constraints.length > 0) { |
||||
for (Constraint constraint : constraints) { |
||||
constraint.affectAnimation(ad.skeleton, boneAnimation); |
||||
} |
||||
} |
||||
|
||||
anims.put(boneAnimation.getName(), boneAnimation); |
||||
} |
||||
|
||||
// getting meshes
|
||||
Mesh[] meshes = null; |
||||
List<Mesh> meshesList = new ArrayList<Mesh>(); |
||||
List<Spatial> children = node.getChildren(); |
||||
for (Spatial child : children) { |
||||
if (child instanceof Geometry) { |
||||
meshesList.add(((Geometry) child).getMesh()); |
||||
} |
||||
} |
||||
if (meshesList.size() > 0) { |
||||
meshes = meshesList.toArray(new Mesh[meshesList.size()]); |
||||
} |
||||
|
||||
// applying the control to the node
|
||||
SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton); |
||||
AnimControl control = node.getControl(AnimControl.class); |
||||
|
||||
if (control == null) { |
||||
control = new AnimControl(ad.skeleton); |
||||
} else { |
||||
// merging skeletons
|
||||
Skeleton controlSkeleton = control.getSkeleton(); |
||||
int boneIndexIncrease = controlSkeleton.getBoneCount(); |
||||
Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton); |
||||
|
||||
// merging animations
|
||||
HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>(); |
||||
for (String animationName : control.getAnimationNames()) { |
||||
animations.put(animationName, control.getAnim(animationName)); |
||||
} |
||||
for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) { |
||||
BoneAnimation ba = animEntry.getValue(); |
||||
for (int i = 0; i < ba.getTracks().length; ++i) { |
||||
BoneTrack bt = ba.getTracks()[i]; |
||||
int newBoneIndex = bt.getTargetBoneIndex() + boneIndexIncrease; |
||||
ba.getTracks()[i] = new BoneTrack(newBoneIndex, bt.getTimes(), bt.getTranslations(), bt.getRotations(), bt.getScales()); |
||||
} |
||||
animations.put(animEntry.getKey(), animEntry.getValue()); |
||||
} |
||||
|
||||
// replacing the control
|
||||
node.removeControl(control); |
||||
control = new AnimControl(skeleton); |
||||
} |
||||
control.setAnimations(anims); |
||||
node.addControl(control); |
||||
node.addControl(skeletonControl); |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
protected Node applyObjectAnimationModifier(Node node, Modifier modifier, DataRepository dataRepository) { |
||||
AnimData ad = (AnimData) modifier.getJmeModifierRepresentation(); |
||||
|
||||
// TODO: Why is this line here? Why is this needed?
|
||||
// Remove if necessary.
|
||||
//ad.skeleton.getBone(0).setAttachNode(node);
|
||||
|
||||
return this.applyArmatureModifierData(node, modifier, dataRepository); |
||||
} |
||||
|
||||
/** |
||||
* This method applies the array modifier to the node. |
||||
* @param node |
||||
* the object the modifier will be applied to |
||||
* @param modifier |
||||
* the modifier to be applied |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return object node with array modifier applied |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) { |
||||
Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation(); |
||||
int fittype = ((Number) modifierData.get("fittype")).intValue(); |
||||
float[] offset = (float[]) modifierData.get("offset"); |
||||
if (offset == null) {// the node will be repeated several times in the same place
|
||||
offset = new float[]{0.0f, 0.0f, 0.0f}; |
||||
} |
||||
float[] scale = (float[]) modifierData.get("scale"); |
||||
if (scale == null) {// the node will be repeated several times in the same place
|
||||
scale = new float[]{0.0f, 0.0f, 0.0f}; |
||||
} else { |
||||
// getting bounding box
|
||||
node.updateModelBound(); |
||||
BoundingVolume boundingVolume = node.getWorldBound(); |
||||
if (boundingVolume instanceof BoundingBox) { |
||||
scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f; |
||||
scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f; |
||||
scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f; |
||||
} else if (boundingVolume instanceof BoundingSphere) { |
||||
float radius = ((BoundingSphere) boundingVolume).getRadius(); |
||||
scale[0] *= radius * 2.0f; |
||||
scale[1] *= radius * 2.0f; |
||||
scale[2] *= radius * 2.0f; |
||||
} else { |
||||
throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName()); |
||||
} |
||||
} |
||||
|
||||
// adding object's offset
|
||||
float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f}; |
||||
Pointer pOffsetObject = (Pointer) modifierData.get("offsetob"); |
||||
if (pOffsetObject != null) { |
||||
FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); |
||||
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, dataRepository).getTranslation(); |
||||
objectOffset[0] = translation.x; |
||||
objectOffset[1] = translation.y; |
||||
objectOffset[2] = translation.z; |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.log(Level.WARNING, "Problems in blender file structure! Object offset cannot be applied! The problem: {0}", e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
// getting start and end caps
|
||||
Node[] caps = new Node[]{null, null}; |
||||
Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")}; |
||||
for (int i = 0; i < pCaps.length; ++i) { |
||||
if (pCaps[i] != null) { |
||||
caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); |
||||
if (caps[i] != null) { |
||||
caps[i] = (Node) caps[i].clone(); |
||||
} else { |
||||
FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); |
||||
try {// we take the structure in case the object was not yet loaded
|
||||
Structure capStructure = capBlock.getStructure(dataRepository); |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository); |
||||
if (caps[i] == null) { |
||||
LOGGER.log(Level.WARNING, "Cap object ''{0}'' couldn''t be loaded!", capStructure.getName()); |
||||
} |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.log(Level.WARNING, "Problems in blender file structure! Cap object cannot be applied! The problem: {0}", e.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], offset[1] + scale[1] + objectOffset[1], offset[2] + scale[2] + objectOffset[2]); |
||||
|
||||
// getting/calculating repeats amount
|
||||
int count = 0; |
||||
if (fittype == 0) {// Fixed count
|
||||
count = ((Number) modifierData.get("count")).intValue() - 1; |
||||
} else if (fittype == 1) {// Fixed length
|
||||
float length = ((Number) modifierData.get("length")).floatValue(); |
||||
if (translationVector.length() > 0.0f) { |
||||
count = (int) (length / translationVector.length()) - 1; |
||||
} |
||||
} else if (fittype == 2) {// Fit curve
|
||||
throw new IllegalStateException("Fit curve should be transformed to Fixed Length array type!"); |
||||
} else { |
||||
throw new IllegalStateException("Unknown fit type: " + fittype); |
||||
} |
||||
|
||||
// adding translated nodes and caps
|
||||
if (count > 0) { |
||||
Node[] arrayNodes = new Node[count]; |
||||
Vector3f newTranslation = new Vector3f(); |
||||
for (int i = 0; i < count; ++i) { |
||||
newTranslation.addLocal(translationVector); |
||||
Node nodeClone = (Node) node.clone(); |
||||
nodeClone.setLocalTranslation(newTranslation); |
||||
arrayNodes[i] = nodeClone; |
||||
} |
||||
for (Node nodeClone : arrayNodes) { |
||||
node.attachChild(nodeClone); |
||||
} |
||||
if (caps[0] != null) { |
||||
caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector); |
||||
node.attachChild(caps[0]); |
||||
} |
||||
if (caps[1] != null) { |
||||
caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector); |
||||
node.attachChild(caps[1]); |
||||
} |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
/** |
||||
* This method applies the mirror modifier to the node. |
||||
* @param node |
||||
* the object the modifier will be applied to |
||||
* @param modifier |
||||
* the modifier to be applied |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return object node with mirror modifier applied |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
protected Node applyMirrorModifierData(Node node, Modifier modifier, DataRepository dataRepository) { |
||||
Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation(); |
||||
int flag = ((Number) modifierData.get("flag")).intValue(); |
||||
float[] mirrorFactor = new float[]{ |
||||
(flag & 0x08) != 0 ? -1.0f : 1.0f, |
||||
(flag & 0x10) != 0 ? -1.0f : 1.0f, |
||||
(flag & 0x20) != 0 ? -1.0f : 1.0f |
||||
}; |
||||
float[] center = new float[]{0.0f, 0.0f, 0.0f}; |
||||
Pointer pObject = (Pointer) modifierData.get("mirrorob"); |
||||
if (pObject != null) { |
||||
Structure objectStructure; |
||||
try { |
||||
objectStructure = pObject.fetchData(dataRepository.getInputStream()).get(0); |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Node object = (Node) objectHelper.toObject(objectStructure, dataRepository); |
||||
if (object != null) { |
||||
Vector3f translation = object.getWorldTranslation(); |
||||
center[0] = translation.x; |
||||
center[1] = translation.y; |
||||
center[2] = translation.z; |
||||
} |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage()); |
||||
} |
||||
} |
||||
float tolerance = ((Number) modifierData.get("tolerance")).floatValue(); |
||||
boolean mirrorU = (flag & 0x01) != 0; |
||||
boolean mirrorV = (flag & 0x02) != 0; |
||||
// boolean mirrorVGroup = (flag & 0x20) != 0;
|
||||
|
||||
List<Geometry> geometriesToAdd = new ArrayList<Geometry>(); |
||||
for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) { |
||||
if (mirrorFactor[mirrorIndex] == -1.0f) { |
||||
for (Spatial spatial : node.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
Mesh clone = mesh.deepClone(); |
||||
|
||||
// getting buffers
|
||||
FloatBuffer position = mesh.getFloatBuffer(Type.Position); |
||||
FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition); |
||||
|
||||
FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position); |
||||
FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition); |
||||
FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal); |
||||
FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal); |
||||
IntBuffer cloneIndexes = (IntBuffer) clone.getBuffer(Type.Index).getData(); |
||||
|
||||
// modyfying data
|
||||
for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) { |
||||
float value = clonePosition.get(i); |
||||
float d = center[mirrorIndex] - value; |
||||
|
||||
if (Math.abs(d) <= tolerance) { |
||||
clonePosition.put(i, center[mirrorIndex]); |
||||
cloneBindPosePosition.put(i, center[mirrorIndex]); |
||||
position.put(i, center[mirrorIndex]); |
||||
bindPosePosition.put(i, center[mirrorIndex]); |
||||
} else { |
||||
clonePosition.put(i, value + 2.0f * d); |
||||
cloneBindPosePosition.put(i, value + 2.0f * d); |
||||
} |
||||
cloneNormals.put(i, -cloneNormals.get(i)); |
||||
cloneBindPoseNormals.put(i, -cloneNormals.get(i)); |
||||
|
||||
//modifying clone indexes
|
||||
int vertexIndex = (i - mirrorIndex) / 3; |
||||
if (vertexIndex % 3 == 0 && vertexIndex<cloneIndexes.limit()) { |
||||
int index = cloneIndexes.get(vertexIndex + 2); |
||||
cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1)); |
||||
cloneIndexes.put(vertexIndex + 1, index); |
||||
} |
||||
} |
||||
|
||||
if (mirrorU) { |
||||
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData(); |
||||
for (int i = 0; i < cloneUVs.limit(); i += 2) { |
||||
cloneUVs.put(i, 1.0f - cloneUVs.get(i)); |
||||
} |
||||
} |
||||
if (mirrorV) { |
||||
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData(); |
||||
for (int i = 1; i < cloneUVs.limit(); i += 2) { |
||||
cloneUVs.put(i, 1.0f - cloneUVs.get(i)); |
||||
} |
||||
} |
||||
|
||||
Geometry geometry = new Geometry(null, clone); |
||||
geometry.setMaterial(((Geometry) spatial).getMaterial()); |
||||
geometriesToAdd.add(geometry); |
||||
} |
||||
} |
||||
|
||||
// adding meshes to node
|
||||
for (Geometry geometry : geometriesToAdd) { |
||||
node.attachChild(geometry); |
||||
} |
||||
geometriesToAdd.clear(); |
||||
} |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
/** |
||||
* This method merges two skeletons into one. I assume that each skeleton's 0-indexed bone is objectAnimationBone so |
||||
* only one such bone should be placed in the result |
||||
* @param s1 |
||||
* first skeleton |
||||
* @param s2 |
||||
* second skeleton |
||||
* @return merged skeleton |
||||
*/ |
||||
protected Skeleton merge(Skeleton s1, Skeleton s2) { |
||||
List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount() + s2.getBoneCount()); |
||||
for (int i = 0; i < s1.getBoneCount(); ++i) { |
||||
bones.add(s1.getBone(i)); |
||||
} |
||||
for (int i = 1; i < s2.getBoneCount(); ++i) {// ommit objectAnimationBone
|
||||
bones.add(s2.getBone(i)); |
||||
} |
||||
return new Skeleton(bones.toArray(new Bone[bones.size()])); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,216 @@ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map.Entry; |
||||
import java.util.Set; |
||||
|
||||
import com.jme3.animation.AnimControl; |
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
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.Spatial; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.animations.ArmatureHelper; |
||||
import com.jme3.scene.plugins.blender.constraints.Constraint; |
||||
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.objects.ObjectHelper; |
||||
import com.jme3.scene.plugins.ogre.AnimData; |
||||
|
||||
/** |
||||
* This modifier allows to add bone animation to the object. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/* package */class ArmatureModifier extends Modifier { |
||||
|
||||
/** |
||||
* This constructor is only temporary. It will be removed when object |
||||
* animation is implemented in jme. TODO!!!!!!! |
||||
*/ |
||||
/* package */ArmatureModifier() { |
||||
} |
||||
|
||||
/** |
||||
* 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 dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ArmatureModifier(Structure objectStructure, Structure modifierStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object"); |
||||
if (pArmatureObject.isNotNull()) { |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), |
||||
LoadedFeatureDataType.LOADED_STRUCTURE); |
||||
if (armatureObject == null) {// we check this first not to fetch the
|
||||
// structure unnecessary
|
||||
armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0); |
||||
objectHelper.toObject(armatureObject, dataRepository); |
||||
} |
||||
additionalData = armatureObject.getOldMemoryAddress(); |
||||
ArmatureHelper armatureHelper = dataRepository |
||||
.getHelper(ArmatureHelper.class); |
||||
|
||||
// changing bones matrices so that they fit the current object (that
|
||||
// is why we need a copy of a skeleton)
|
||||
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); |
||||
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); |
||||
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); |
||||
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); |
||||
|
||||
// setting the bones structure inside the skeleton (thus completing
|
||||
// its loading)
|
||||
Skeleton skeleton = new Skeleton(bones); |
||||
dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton); |
||||
|
||||
String objectName = objectStructure.getName(); |
||||
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); |
||||
if (animationNames != null && animationNames.size() > 0) { |
||||
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>(); |
||||
List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); |
||||
for (FileBlockHeader header : actionHeaders) { |
||||
Structure actionStructure = header.getStructure(dataRepository); |
||||
String actionName = actionStructure.getName(); |
||||
if (animationNames.contains(actionName)) { |
||||
int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName); |
||||
int fps = dataRepository.getBlenderKey().getFps(); |
||||
float start = (float) animationFrames[0] / (float) fps; |
||||
float stop = (float) animationFrames[1] / (float) fps; |
||||
BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start); |
||||
boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName)); |
||||
animations.add(boneAnimation); |
||||
} |
||||
} |
||||
jmeModifierRepresentation = new AnimData(new Skeleton(bones), animations); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Node apply(Node node, DataRepository dataRepository) { |
||||
if(jmeModifierRepresentation == null) { |
||||
return node; |
||||
} |
||||
AnimData ad = (AnimData) jmeModifierRepresentation; |
||||
ArrayList<BoneAnimation> animList = ad.anims; |
||||
Long modifierArmatureObject = (Long) additionalData; |
||||
if (animList != null && animList.size() > 0) { |
||||
List<Constraint> constraints = dataRepository.getConstraints(modifierArmatureObject); |
||||
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>(); |
||||
for (int i = 0; i < animList.size(); ++i) { |
||||
BoneAnimation boneAnimation = animList.get(i).clone(); |
||||
|
||||
// baking constraints into animations
|
||||
if (constraints != null && constraints.size() > 0) { |
||||
for (Constraint constraint : constraints) { |
||||
constraint.affectAnimation(ad.skeleton, boneAnimation); |
||||
} |
||||
} |
||||
|
||||
anims.put(boneAnimation.getName(), boneAnimation); |
||||
} |
||||
|
||||
// getting meshes
|
||||
Mesh[] meshes = null; |
||||
List<Mesh> meshesList = new ArrayList<Mesh>(); |
||||
List<Spatial> children = node.getChildren(); |
||||
for (Spatial child : children) { |
||||
if (child instanceof Geometry) { |
||||
meshesList.add(((Geometry) child).getMesh()); |
||||
} |
||||
} |
||||
if (meshesList.size() > 0) { |
||||
meshes = meshesList.toArray(new Mesh[meshesList.size()]); |
||||
} |
||||
|
||||
// applying the control to the node
|
||||
SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton); |
||||
AnimControl control = node.getControl(AnimControl.class); |
||||
|
||||
if (control == null) { |
||||
control = new AnimControl(ad.skeleton); |
||||
} else { |
||||
// merging skeletons
|
||||
Skeleton controlSkeleton = control.getSkeleton(); |
||||
int boneIndexIncrease = controlSkeleton.getBoneCount(); |
||||
Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton); |
||||
|
||||
// merging animations
|
||||
HashMap<String, BoneAnimation> animations = new HashMap<String, BoneAnimation>(); |
||||
for (String animationName : control.getAnimationNames()) { |
||||
animations.put(animationName, |
||||
control.getAnim(animationName)); |
||||
} |
||||
for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) { |
||||
BoneAnimation ba = animEntry.getValue(); |
||||
for (int i = 0; i < ba.getTracks().length; ++i) { |
||||
BoneTrack bt = ba.getTracks()[i]; |
||||
int newBoneIndex = bt.getTargetBoneIndex() |
||||
+ boneIndexIncrease; |
||||
ba.getTracks()[i] = new BoneTrack(newBoneIndex, |
||||
bt.getTimes(), bt.getTranslations(), |
||||
bt.getRotations(), bt.getScales()); |
||||
} |
||||
animations.put(animEntry.getKey(), animEntry.getValue()); |
||||
} |
||||
|
||||
// replacing the control
|
||||
node.removeControl(control); |
||||
control = new AnimControl(skeleton); |
||||
} |
||||
control.setAnimations(anims); |
||||
node.addControl(control); |
||||
node.addControl(skeletonControl); |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
@Override |
||||
public String getType() { |
||||
return Modifier.ARMATURE_MODIFIER_DATA; |
||||
} |
||||
|
||||
/** |
||||
* This method merges two skeletons into one. I assume that each skeleton's |
||||
* 0-indexed bone is objectAnimationBone so only one such bone should be |
||||
* placed in the result |
||||
* |
||||
* @param s1 |
||||
* first skeleton |
||||
* @param s2 |
||||
* second skeleton |
||||
* @return merged skeleton |
||||
*/ |
||||
protected Skeleton merge(Skeleton s1, Skeleton s2) { |
||||
List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount() |
||||
+ s2.getBoneCount()); |
||||
for (int i = 0; i < s1.getBoneCount(); ++i) { |
||||
bones.add(s1.getBone(i)); |
||||
} |
||||
for (int i = 1; i < s2.getBoneCount(); ++i) {// ommit
|
||||
// objectAnimationBone
|
||||
bones.add(s2.getBone(i)); |
||||
} |
||||
return new Skeleton(bones.toArray(new Bone[bones.size()])); |
||||
} |
||||
} |
@ -0,0 +1,244 @@ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.bounding.BoundingBox; |
||||
import com.jme3.bounding.BoundingSphere; |
||||
import com.jme3.bounding.BoundingVolume; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.DynamicArray; |
||||
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.objects.ObjectHelper; |
||||
import com.jme3.scene.shape.Curve; |
||||
|
||||
/** |
||||
* This modifier allows to array modifier to the object. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class ArrayModifier extends Modifier { |
||||
private static final Logger LOGGER = Logger.getLogger(ArrayModifier.class.getName()); |
||||
|
||||
/** |
||||
* This constructor reads array data from the modifier structure. The |
||||
* stored data is a map of parameters for array modifier. No additional data |
||||
* is loaded. |
||||
* |
||||
* @param objectStructure |
||||
* the structure of the object |
||||
* @param modifierStructure |
||||
* the structure of the modifier |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public ArrayModifier(Structure modifier, DataRepository dataRepository) throws BlenderFileException { |
||||
Map<String, Object> params = new HashMap<String, Object>(); |
||||
|
||||
Number fittype = (Number) modifier.getFieldValue("fit_type"); |
||||
params.put("fittype", fittype); |
||||
switch (fittype.intValue()) { |
||||
case 0:// FIXED COUNT
|
||||
params.put("count", modifier.getFieldValue("count")); |
||||
break; |
||||
case 1:// FIXED LENGTH
|
||||
params.put("length", modifier.getFieldValue("length")); |
||||
break; |
||||
case 2:// FITCURVE
|
||||
Pointer pCurveOb = (Pointer) modifier.getFieldValue("curve_ob"); |
||||
float length = 0; |
||||
if (pCurveOb.isNotNull()) { |
||||
Structure curveStructure = pCurveOb.fetchData(dataRepository.getInputStream()).get(0); |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Node curveObject = (Node) objectHelper.toObject(curveStructure, dataRepository); |
||||
Set<Number> referencesToCurveLengths = new HashSet<Number>(curveObject.getChildren().size()); |
||||
for (Spatial spatial : curveObject.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
if (mesh instanceof Curve) { |
||||
length += ((Curve) mesh).getLength(); |
||||
} else { |
||||
//if bevel object has several parts then each mesh will have the same reference
|
||||
//to length value (and we should use only one)
|
||||
Number curveLength = spatial.getUserData("curveLength"); |
||||
if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) { |
||||
length += curveLength.floatValue(); |
||||
referencesToCurveLengths.add(curveLength); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
params.put("length", Float.valueOf(length)); |
||||
params.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH
|
||||
break; |
||||
default: |
||||
assert false : "Unknown array modifier fit type: " + fittype; |
||||
} |
||||
|
||||
// offset parameters
|
||||
int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue(); |
||||
if ((offsettype & 0x01) != 0) {// Constant offset
|
||||
DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifier.getFieldValue("offset"); |
||||
float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()}; |
||||
params.put("offset", offset); |
||||
} |
||||
if ((offsettype & 0x02) != 0) {// Relative offset
|
||||
DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifier.getFieldValue("scale"); |
||||
float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()}; |
||||
params.put("scale", scale); |
||||
} |
||||
if ((offsettype & 0x04) != 0) {// Object offset
|
||||
Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob"); |
||||
if (pOffsetObject.isNotNull()) { |
||||
params.put("offsetob", pOffsetObject); |
||||
} |
||||
} |
||||
|
||||
// start cap and end cap
|
||||
Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap"); |
||||
if (pStartCap.isNotNull()) { |
||||
params.put("startcap", pStartCap); |
||||
} |
||||
Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap"); |
||||
if (pEndCap.isNotNull()) { |
||||
params.put("endcap", pEndCap); |
||||
} |
||||
jmeModifierRepresentation = params; |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("unchecked") |
||||
public Node apply(Node node, DataRepository dataRepository) { |
||||
Map<String, Object> modifierData = (Map<String, Object>) jmeModifierRepresentation; |
||||
int fittype = ((Number) modifierData.get("fittype")).intValue(); |
||||
float[] offset = (float[]) modifierData.get("offset"); |
||||
if (offset == null) {// the node will be repeated several times in the same place
|
||||
offset = new float[]{0.0f, 0.0f, 0.0f}; |
||||
} |
||||
float[] scale = (float[]) modifierData.get("scale"); |
||||
if (scale == null) {// the node will be repeated several times in the same place
|
||||
scale = new float[]{0.0f, 0.0f, 0.0f}; |
||||
} else { |
||||
// getting bounding box
|
||||
node.updateModelBound(); |
||||
BoundingVolume boundingVolume = node.getWorldBound(); |
||||
if (boundingVolume instanceof BoundingBox) { |
||||
scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f; |
||||
scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f; |
||||
scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f; |
||||
} else if (boundingVolume instanceof BoundingSphere) { |
||||
float radius = ((BoundingSphere) boundingVolume).getRadius(); |
||||
scale[0] *= radius * 2.0f; |
||||
scale[1] *= radius * 2.0f; |
||||
scale[2] *= radius * 2.0f; |
||||
} else { |
||||
throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName()); |
||||
} |
||||
} |
||||
|
||||
// adding object's offset
|
||||
float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f}; |
||||
Pointer pOffsetObject = (Pointer) modifierData.get("offsetob"); |
||||
if (pOffsetObject != null) { |
||||
FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); |
||||
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, dataRepository).getTranslation(); |
||||
objectOffset[0] = translation.x; |
||||
objectOffset[1] = translation.y; |
||||
objectOffset[2] = translation.z; |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.log(Level.WARNING, "Problems in blender file structure! Object offset cannot be applied! The problem: {0}", e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
// getting start and end caps
|
||||
Node[] caps = new Node[]{null, null}; |
||||
Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")}; |
||||
for (int i = 0; i < pCaps.length; ++i) { |
||||
if (pCaps[i] != null) { |
||||
caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); |
||||
if (caps[i] != null) { |
||||
caps[i] = (Node) caps[i].clone(); |
||||
} else { |
||||
FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress()); |
||||
try {// we take the structure in case the object was not yet loaded
|
||||
Structure capStructure = capBlock.getStructure(dataRepository); |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository); |
||||
if (caps[i] == null) { |
||||
LOGGER.log(Level.WARNING, "Cap object ''{0}'' couldn''t be loaded!", capStructure.getName()); |
||||
} |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.log(Level.WARNING, "Problems in blender file structure! Cap object cannot be applied! The problem: {0}", e.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], offset[1] + scale[1] + objectOffset[1], offset[2] + scale[2] + objectOffset[2]); |
||||
|
||||
// getting/calculating repeats amount
|
||||
int count = 0; |
||||
if (fittype == 0) {// Fixed count
|
||||
count = ((Number) modifierData.get("count")).intValue() - 1; |
||||
} else if (fittype == 1) {// Fixed length
|
||||
float length = ((Number) modifierData.get("length")).floatValue(); |
||||
if (translationVector.length() > 0.0f) { |
||||
count = (int) (length / translationVector.length()) - 1; |
||||
} |
||||
} else if (fittype == 2) {// Fit curve
|
||||
throw new IllegalStateException("Fit curve should be transformed to Fixed Length array type!"); |
||||
} else { |
||||
throw new IllegalStateException("Unknown fit type: " + fittype); |
||||
} |
||||
|
||||
// adding translated nodes and caps
|
||||
if (count > 0) { |
||||
Node[] arrayNodes = new Node[count]; |
||||
Vector3f newTranslation = new Vector3f(); |
||||
for (int i = 0; i < count; ++i) { |
||||
newTranslation.addLocal(translationVector); |
||||
Node nodeClone = (Node) node.clone(); |
||||
nodeClone.setLocalTranslation(newTranslation); |
||||
arrayNodes[i] = nodeClone; |
||||
} |
||||
for (Node nodeClone : arrayNodes) { |
||||
node.attachChild(nodeClone); |
||||
} |
||||
if (caps[0] != null) { |
||||
caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector); |
||||
node.attachChild(caps[0]); |
||||
} |
||||
if (caps[1] != null) { |
||||
caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector); |
||||
node.attachChild(caps[1]); |
||||
} |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
@Override |
||||
public String getType() { |
||||
return ARRAY_MODIFIER_DATA; |
||||
} |
||||
} |
@ -0,0 +1,170 @@ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
import java.nio.FloatBuffer; |
||||
import java.nio.IntBuffer; |
||||
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.math.Vector3f; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.VertexBuffer.Type; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Pointer; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
import com.jme3.scene.plugins.blender.objects.ObjectHelper; |
||||
|
||||
/** |
||||
* This modifier allows to array modifier to the object. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/*package*/ class MirrorModifier extends Modifier { |
||||
private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName()); |
||||
|
||||
/** |
||||
* This constructor reads mirror data from the modifier structure. The |
||||
* stored data is a map of parameters for mirror modifier. No additional data |
||||
* is loaded. |
||||
* When the modifier is applied it is necessary to get the newly created node. |
||||
* |
||||
* @param objectStructure |
||||
* the structure of the object |
||||
* @param modifierStructure |
||||
* the structure of the modifier |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public MirrorModifier(Structure modifier, DataRepository dataRepository) { |
||||
Map<String, Object> params = new HashMap<String, Object>(); |
||||
|
||||
params.put("flag", modifier.getFieldValue("flag")); |
||||
params.put("tolerance", modifier.getFieldValue("tolerance")); |
||||
Pointer pMirrorOb = (Pointer) modifier.getFieldValue("mirror_ob"); |
||||
if (pMirrorOb.isNotNull()) { |
||||
params.put("mirrorob", pMirrorOb); |
||||
} |
||||
jmeModifierRepresentation = params; |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("unchecked") |
||||
public Node apply(Node node, DataRepository dataRepository) { |
||||
Map<String, Object> modifierData = (Map<String, Object>) jmeModifierRepresentation; |
||||
int flag = ((Number) modifierData.get("flag")).intValue(); |
||||
float[] mirrorFactor = new float[]{ |
||||
(flag & 0x08) != 0 ? -1.0f : 1.0f, |
||||
(flag & 0x10) != 0 ? -1.0f : 1.0f, |
||||
(flag & 0x20) != 0 ? -1.0f : 1.0f |
||||
}; |
||||
float[] center = new float[]{0.0f, 0.0f, 0.0f}; |
||||
Pointer pObject = (Pointer) modifierData.get("mirrorob"); |
||||
if (pObject != null) { |
||||
Structure objectStructure; |
||||
try { |
||||
objectStructure = pObject.fetchData(dataRepository.getInputStream()).get(0); |
||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); |
||||
Node object = (Node) objectHelper.toObject(objectStructure, dataRepository); |
||||
if (object != null) { |
||||
Vector3f translation = object.getWorldTranslation(); |
||||
center[0] = translation.x; |
||||
center[1] = translation.y; |
||||
center[2] = translation.z; |
||||
} |
||||
} catch (BlenderFileException e) { |
||||
LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage()); |
||||
} |
||||
} |
||||
float tolerance = ((Number) modifierData.get("tolerance")).floatValue(); |
||||
boolean mirrorU = (flag & 0x01) != 0; |
||||
boolean mirrorV = (flag & 0x02) != 0; |
||||
// boolean mirrorVGroup = (flag & 0x20) != 0;
|
||||
|
||||
List<Geometry> geometriesToAdd = new ArrayList<Geometry>(); |
||||
for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) { |
||||
if (mirrorFactor[mirrorIndex] == -1.0f) { |
||||
for (Spatial spatial : node.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
Mesh clone = mesh.deepClone(); |
||||
|
||||
// getting buffers
|
||||
FloatBuffer position = mesh.getFloatBuffer(Type.Position); |
||||
FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition); |
||||
|
||||
FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position); |
||||
FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition); |
||||
FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal); |
||||
FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal); |
||||
IntBuffer cloneIndexes = (IntBuffer) clone.getBuffer(Type.Index).getData(); |
||||
|
||||
// modyfying data
|
||||
for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) { |
||||
float value = clonePosition.get(i); |
||||
float d = center[mirrorIndex] - value; |
||||
|
||||
if (Math.abs(d) <= tolerance) { |
||||
clonePosition.put(i, center[mirrorIndex]); |
||||
cloneBindPosePosition.put(i, center[mirrorIndex]); |
||||
position.put(i, center[mirrorIndex]); |
||||
bindPosePosition.put(i, center[mirrorIndex]); |
||||
} else { |
||||
clonePosition.put(i, value + 2.0f * d); |
||||
cloneBindPosePosition.put(i, value + 2.0f * d); |
||||
} |
||||
cloneNormals.put(i, -cloneNormals.get(i)); |
||||
cloneBindPoseNormals.put(i, -cloneNormals.get(i)); |
||||
|
||||
//modifying clone indexes
|
||||
int vertexIndex = (i - mirrorIndex) / 3; |
||||
if (vertexIndex % 3 == 0 && vertexIndex<cloneIndexes.limit()) { |
||||
int index = cloneIndexes.get(vertexIndex + 2); |
||||
cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1)); |
||||
cloneIndexes.put(vertexIndex + 1, index); |
||||
} |
||||
} |
||||
|
||||
if (mirrorU) { |
||||
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData(); |
||||
for (int i = 0; i < cloneUVs.limit(); i += 2) { |
||||
cloneUVs.put(i, 1.0f - cloneUVs.get(i)); |
||||
} |
||||
} |
||||
if (mirrorV) { |
||||
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData(); |
||||
for (int i = 1; i < cloneUVs.limit(); i += 2) { |
||||
cloneUVs.put(i, 1.0f - cloneUVs.get(i)); |
||||
} |
||||
} |
||||
|
||||
Geometry geometry = new Geometry(null, clone); |
||||
geometry.setMaterial(((Geometry) spatial).getMaterial()); |
||||
geometriesToAdd.add(geometry); |
||||
} |
||||
} |
||||
|
||||
// adding meshes to node
|
||||
for (Geometry geometry : geometriesToAdd) { |
||||
node.attachChild(geometry); |
||||
} |
||||
geometriesToAdd.clear(); |
||||
} |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
@Override |
||||
public String getType() { |
||||
return Modifier.MIRROR_MODIFIER_DATA; |
||||
} |
||||
} |
@ -0,0 +1,67 @@ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
|
||||
/** |
||||
* This class represents an object's modifier. The modifier object can be varied |
||||
* and the user needs to know what is the type of it for the specified type |
||||
* name. For example "ArmatureModifierData" type specified in blender is |
||||
* represented by AnimData object from jMonkeyEngine. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
public abstract class Modifier { |
||||
|
||||
public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData"; |
||||
public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData"; |
||||
public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData"; |
||||
public static final String MIRROR_MODIFIER_DATA = "MirrorModifierData"; |
||||
public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData"; |
||||
|
||||
/** |
||||
* JME modifier representation object. |
||||
*/ |
||||
protected Object jmeModifierRepresentation; |
||||
|
||||
/** |
||||
* Various additional data used by modifiers. |
||||
*/ |
||||
protected Object additionalData; |
||||
|
||||
/** |
||||
* This method returns JME modifier representation object. |
||||
* |
||||
* @return JME modifier representation object |
||||
*/ |
||||
public Object getJmeModifierRepresentation() { |
||||
return jmeModifierRepresentation; |
||||
} |
||||
|
||||
/** |
||||
* This method returns additional data stored in the modifier. |
||||
* |
||||
* @return the additional data stored in the modifier |
||||
*/ |
||||
public Object getAdditionalData() { |
||||
return additionalData; |
||||
} |
||||
|
||||
/** |
||||
* This method applies the modifier to the given node. |
||||
* |
||||
* @param node |
||||
* the node that will have modifier applied |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return the node with applied modifier |
||||
*/ |
||||
public abstract Node apply(Node node, DataRepository dataRepository); |
||||
|
||||
/** |
||||
* This method returns blender's type of modifier. |
||||
* |
||||
* @return blender's type of modifier |
||||
*/ |
||||
public abstract String getType(); |
||||
} |
@ -0,0 +1,113 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
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.scene.plugins.blender.AbstractBlenderHelper; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Pointer; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
|
||||
/** |
||||
* A class that is used in modifiers calculations. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class ModifierHelper extends AbstractBlenderHelper { |
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName()); |
||||
|
||||
/** |
||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in |
||||
* different blender versions. |
||||
* @param blenderVersion |
||||
* the version read from the blend file |
||||
*/ |
||||
public ModifierHelper(String blenderVersion) { |
||||
super(blenderVersion); |
||||
} |
||||
|
||||
/** |
||||
* This method reads the given object's modifiers. |
||||
* @param objectStructure |
||||
* the object structure |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @param converter |
||||
* the converter object (in some cases we need to read an object first before loading the modifier) |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow corrupted |
||||
*/ |
||||
public Collection<Modifier> readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
Collection<Modifier> result = new ArrayList<Modifier>(); |
||||
Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers"); |
||||
List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository); |
||||
for (Structure modifierStructure : modifiers) { |
||||
Modifier modifier = null; |
||||
if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) { |
||||
modifier = new ArrayModifier(modifierStructure, dataRepository); |
||||
} else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) { |
||||
modifier = new MirrorModifier(modifierStructure, dataRepository); |
||||
} else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) { |
||||
modifier = new ArmatureModifier(objectStructure, modifierStructure, dataRepository); |
||||
} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) { |
||||
modifier = new ParticlesModifier(modifierStructure, dataRepository); |
||||
} |
||||
|
||||
if(modifier != null) { |
||||
result.add(modifier); |
||||
dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier); |
||||
} else { |
||||
LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType()); |
||||
} |
||||
} |
||||
|
||||
//at the end read object's animation modifier
|
||||
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo"); |
||||
if (pIpo.isNotNull()) { |
||||
Modifier modifier = new ObjectAnimationModifier(objectStructure, dataRepository); |
||||
result.add(modifier); |
||||
dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,122 @@ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Transform; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.animations.Ipo; |
||||
import com.jme3.scene.plugins.blender.animations.IpoHelper; |
||||
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.objects.ObjectHelper; |
||||
import com.jme3.scene.plugins.ogre.AnimData; |
||||
|
||||
/** |
||||
* This modifier allows to add animation to the object. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/* package */class ObjectAnimationModifier extends ArmatureModifier { |
||||
|
||||
/** |
||||
* This constructor reads animation of the object itself (without bones) and |
||||
* stores it as an ArmatureModifierData modifier. The animation is returned |
||||
* as a modifier. It should be later applied regardless other modifiers. The |
||||
* reason for this is that object may not have modifiers added but it's |
||||
* animation should be working. The stored modifier is an anim data and |
||||
* additional data is given object's OMA. |
||||
* |
||||
* @param objectStructure |
||||
* the structure of the object |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @return animation modifier is returned, it should be separately applied |
||||
* when the object is loaded |
||||
* @throws BlenderFileException |
||||
* this exception is thrown when the blender file is somehow |
||||
* corrupted |
||||
*/ |
||||
public ObjectAnimationModifier(Structure objectStructure, |
||||
DataRepository dataRepository) throws BlenderFileException { |
||||
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo"); |
||||
if (pIpo.isNotNull()) { |
||||
// check if there is an action name connected with this ipo
|
||||
String objectAnimationName = null; |
||||
List<FileBlockHeader> actionBlocks = dataRepository |
||||
.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); |
||||
for (FileBlockHeader actionBlock : actionBlocks) { |
||||
Structure action = actionBlock.getStructure(dataRepository); |
||||
List<Structure> actionChannels = ((Structure) action |
||||
.getFieldValue("chanbase")) |
||||
.evaluateListBase(dataRepository); |
||||
if (actionChannels.size() == 1) {// object's animtion action has
|
||||
// only one channel
|
||||
Pointer pChannelIpo = (Pointer) actionChannels.get(0) |
||||
.getFieldValue("ipo"); |
||||
if (pChannelIpo.equals(pIpo)) { |
||||
objectAnimationName = action.getName(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
String objectName = objectStructure.getName(); |
||||
if (objectAnimationName == null) {// set the object's animation name
|
||||
// to object's name
|
||||
objectAnimationName = objectName; |
||||
} |
||||
|
||||
IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); |
||||
Structure ipoStructure = pIpo.fetchData( |
||||
dataRepository.getInputStream()).get(0); |
||||
Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); |
||||
int[] animationFrames = dataRepository.getBlenderKey() |
||||
.getAnimationFrames(objectName, objectAnimationName); |
||||
if (animationFrames == null) {// if the name was created here there
|
||||
// are no frames set for the
|
||||
// animation
|
||||
animationFrames = new int[] { 1, ipo.getLastFrame() }; |
||||
} |
||||
int fps = dataRepository.getBlenderKey().getFps(); |
||||
float start = (float) animationFrames[0] / (float) fps; |
||||
float stop = (float) animationFrames[1] / (float) fps; |
||||
|
||||
// calculating track for the only bone in this skeleton
|
||||
BoneTrack[] tracks = new BoneTrack[1]; |
||||
tracks[0] = ipo.calculateTrack(0, animationFrames[0], |
||||
animationFrames[1], fps); |
||||
|
||||
BoneAnimation boneAnimation = new BoneAnimation( |
||||
objectAnimationName, stop - start); |
||||
boneAnimation.setTracks(tracks); |
||||
ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>( |
||||
1); |
||||
animations.add(boneAnimation); |
||||
|
||||
// preparing the object's bone
|
||||
ObjectHelper objectHelper = dataRepository |
||||
.getHelper(ObjectHelper.class); |
||||
Transform t = objectHelper.getTransformation(objectStructure, |
||||
dataRepository); |
||||
Bone bone = new Bone(null); |
||||
bone.setBindTransforms(t.getTranslation(), t.getRotation(), |
||||
t.getScale()); |
||||
|
||||
jmeModifierRepresentation = new AnimData(new Skeleton( |
||||
new Bone[] { bone }), animations); |
||||
additionalData = objectStructure.getOldMemoryAddress(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String getType() { |
||||
return Modifier.OBJECT_ANIMATION_MODIFIER_DATA; |
||||
} |
||||
} |
@ -0,0 +1,99 @@ |
||||
package com.jme3.scene.plugins.blender.modifiers; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import com.jme3.effect.ParticleEmitter; |
||||
import com.jme3.effect.shapes.EmitterMeshVertexShape; |
||||
import com.jme3.effect.shapes.EmitterShape; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.plugins.blender.DataRepository; |
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.file.Pointer; |
||||
import com.jme3.scene.plugins.blender.file.Structure; |
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper; |
||||
import com.jme3.scene.plugins.blender.particles.ParticlesHelper; |
||||
|
||||
/** |
||||
* This modifier allows to add particles to the object. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
/* package */class ParticlesModifier extends Modifier { |
||||
|
||||
/** |
||||
* This constructor reads the particles system structure and stores it in |
||||
* order to apply it later to the node. |
||||
* |
||||
* @param modifier |
||||
* the structure of the modifier |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* an exception is throw wneh there are problems with the |
||||
* blender file |
||||
*/ |
||||
public ParticlesModifier(Structure modifier, DataRepository dataRepository) |
||||
throws BlenderFileException { |
||||
Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys"); |
||||
if (pParticleSystem.isNotNull()) { |
||||
ParticlesHelper particlesHelper = dataRepository |
||||
.getHelper(ParticlesHelper.class); |
||||
Structure particleSystem = pParticleSystem.fetchData( |
||||
dataRepository.getInputStream()).get(0); |
||||
jmeModifierRepresentation = particlesHelper.toParticleEmitter( |
||||
particleSystem, dataRepository); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Node apply(Node node, DataRepository dataRepository) { |
||||
MaterialHelper materialHelper = dataRepository |
||||
.getHelper(MaterialHelper.class); |
||||
ParticleEmitter emitter = (ParticleEmitter) jmeModifierRepresentation; |
||||
emitter = emitter.clone(); |
||||
|
||||
// veryfying the alpha function for particles' texture
|
||||
Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE; |
||||
char nameSuffix = emitter.getName().charAt( |
||||
emitter.getName().length() - 1); |
||||
if (nameSuffix == 'B' || nameSuffix == 'N') { |
||||
alphaFunction = MaterialHelper.ALPHA_MASK_NONE; |
||||
} |
||||
// removing the type suffix from the name
|
||||
emitter.setName(emitter.getName().substring(0, |
||||
emitter.getName().length() - 1)); |
||||
|
||||
// applying emitter shape
|
||||
EmitterShape emitterShape = emitter.getShape(); |
||||
List<Mesh> meshes = new ArrayList<Mesh>(); |
||||
for (Spatial spatial : node.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Mesh mesh = ((Geometry) spatial).getMesh(); |
||||
if (mesh != null) { |
||||
meshes.add(mesh); |
||||
Material material = materialHelper.getParticlesMaterial( |
||||
((Geometry) spatial).getMaterial(), alphaFunction, |
||||
dataRepository); |
||||
emitter.setMaterial(material);// TODO: divide into several
|
||||
// pieces
|
||||
} |
||||
} |
||||
} |
||||
if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) { |
||||
((EmitterMeshVertexShape) emitterShape).setMeshes(meshes); |
||||
} |
||||
|
||||
node.attachChild(emitter); |
||||
return node; |
||||
} |
||||
|
||||
@Override |
||||
public String getType() { |
||||
return Modifier.PARTICLE_MODIFIER_DATA; |
||||
} |
||||
} |
@ -1,230 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.structures; |
||||
|
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.BoneTrack; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.helpers.v249.ObjectHelper; |
||||
import com.jme3.scene.plugins.blender.structures.Constraint.Space; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
|
||||
/** |
||||
* This class is used to calculate the constraint. The following methods should be implemented: affectLocation, |
||||
* affectRotation and affectScale. This class also defines all constants required by known deriving classes. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public abstract class AbstractInfluenceFunction { |
||||
|
||||
protected static final Logger LOGGER = Logger.getLogger(AbstractInfluenceFunction.class.getName()); |
||||
protected static final float IK_SOLVER_ERROR = 0.5f; |
||||
//DISTLIMIT
|
||||
protected static final int LIMITDIST_INSIDE = 0; |
||||
protected static final int LIMITDIST_OUTSIDE = 1; |
||||
protected static final int LIMITDIST_ONSURFACE = 2; |
||||
//CONSTRAINT_TYPE_LOCLIKE
|
||||
protected static final int LOCLIKE_X = 0x01; |
||||
protected static final int LOCLIKE_Y = 0x02; |
||||
protected static final int LOCLIKE_Z = 0x04; |
||||
//ROTLIKE
|
||||
protected static final int ROTLIKE_X = 0x01; |
||||
protected static final int ROTLIKE_Y = 0x02; |
||||
protected static final int ROTLIKE_Z = 0x04; |
||||
protected static final int ROTLIKE_X_INVERT = 0x10; |
||||
protected static final int ROTLIKE_Y_INVERT = 0x20; |
||||
protected static final int ROTLIKE_Z_INVERT = 0x40; |
||||
protected static final int ROTLIKE_OFFSET = 0x80; |
||||
//SIZELIKE
|
||||
protected static final int SIZELIKE_X = 0x01; |
||||
protected static final int SIZELIKE_Y = 0x02; |
||||
protected static final int SIZELIKE_Z = 0x04; |
||||
protected static final int SIZELIKE_OFFSET = 0x80; |
||||
|
||||
/* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */ |
||||
//protected static final int LOCLIKE_TIP = 0x08;
|
||||
protected static final int LOCLIKE_X_INVERT = 0x10; |
||||
protected static final int LOCLIKE_Y_INVERT = 0x20; |
||||
protected static final int LOCLIKE_Z_INVERT = 0x40; |
||||
protected static final int LOCLIKE_OFFSET = 0x80; |
||||
//LOCLIMIT, SIZELIMIT
|
||||
protected static final int LIMIT_XMIN = 0x01; |
||||
protected static final int LIMIT_XMAX = 0x02; |
||||
protected static final int LIMIT_YMIN = 0x04; |
||||
protected static final int LIMIT_YMAX = 0x08; |
||||
protected static final int LIMIT_ZMIN = 0x10; |
||||
protected static final int LIMIT_ZMAX = 0x20; |
||||
//ROTLIMIT
|
||||
protected static final int LIMIT_XROT = 0x01; |
||||
protected static final int LIMIT_YROT = 0x02; |
||||
protected static final int LIMIT_ZROT = 0x04; |
||||
/** The type of the constraint. */ |
||||
protected ConstraintType constraintType; |
||||
/** The data repository. */ |
||||
protected DataRepository dataRepository; |
||||
|
||||
/** |
||||
* Constructor. |
||||
* @param constraintType |
||||
* the type of the current constraint |
||||
* @param dataRepository |
||||
* the data repository |
||||
*/ |
||||
public AbstractInfluenceFunction(ConstraintType constraintType, DataRepository dataRepository) { |
||||
this.constraintType = constraintType; |
||||
this.dataRepository = dataRepository; |
||||
} |
||||
|
||||
/** |
||||
* This method validates the constraint type. It throws an IllegalArgumentException if the constraint type of the |
||||
* given structure is invalid. |
||||
* @param constraintStructure |
||||
* the structure with constraint data |
||||
*/ |
||||
protected void validateConstraintType(Structure constraintStructure) { |
||||
if (!constraintType.getClassName().equalsIgnoreCase(constraintStructure.getType())) { |
||||
throw new IllegalArgumentException("Invalud structure type (" + constraintStructure.getType() + ") for the constraint: " + constraintType.getClassName() + '!'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method affects the bone animation tracks for the given skeleton. |
||||
* @param skeleton |
||||
* the skeleton containing the affected bones by constraint |
||||
* @param boneAnimation |
||||
* the bone animation baked traces |
||||
* @param constraint |
||||
* the constraint |
||||
*/ |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
} |
||||
|
||||
/** |
||||
* This method returns the bone traces for the bone that is affected by the given constraint. |
||||
* @param skeleton |
||||
* the skeleton containing bones |
||||
* @param boneAnimation |
||||
* the bone animation that affects the skeleton |
||||
* @param constraint |
||||
* the affecting constraint |
||||
* @return the bone track for the bone that is being affected by the constraint |
||||
*/ |
||||
protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) { |
||||
Long boneOMA = constraint.getBoneOMA(); |
||||
Bone bone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
int boneIndex = bone==null ? 0 : skeleton.getBoneIndex(bone);//bone==null may mean the object animation
|
||||
if (boneIndex != -1) { |
||||
//searching for track for this bone
|
||||
for (BoneTrack boneTrack : boneAnimation.getTracks()) { |
||||
if (boneTrack.getTargetBoneIndex() == boneIndex) { |
||||
return boneTrack; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the target or subtarget object (if specified). |
||||
* @param constraint |
||||
* the constraint instance |
||||
* @return target or subtarget feature |
||||
* @throws BlenderFileException this exception is thrown if the blend file is somehow corrupted |
||||
*/ |
||||
protected Object getTarget(Constraint constraint, LoadedFeatureDataType loadedFeatureDataType) throws BlenderFileException { |
||||
//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); |
||||
//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) { |
||||
result = dataRepository.getLoadedFeature(subtargetName, loadedFeatureDataType); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object location. |
||||
* @param constraint |
||||
* the constraint instance |
||||
* @return target's object location |
||||
*/ |
||||
protected Vector3f getTargetLocation(Constraint constraint) { |
||||
Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); |
||||
Space targetSpace = constraint.getTargetSpace(); |
||||
Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
switch (targetSpace) { |
||||
case CONSTRAINT_SPACE_LOCAL: |
||||
return targetObject.getLocalTranslation(); |
||||
case CONSTRAINT_SPACE_WORLD: |
||||
return targetObject.getWorldTranslation(); |
||||
default: |
||||
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object location in the specified frame. |
||||
* @param constraint |
||||
* the constraint instance |
||||
* @param frame |
||||
* the frame number |
||||
* @return target's object location |
||||
*/ |
||||
protected Vector3f getTargetLocation(Constraint constraint, int frame) { |
||||
return this.getTargetLocation(constraint);//TODO: implement getting location in a specified frame
|
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object rotation. |
||||
* @param constraint |
||||
* the constraint instance |
||||
* @return target's object rotation |
||||
*/ |
||||
protected Quaternion getTargetRotation(Constraint constraint) { |
||||
Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); |
||||
Space targetSpace = constraint.getTargetSpace(); |
||||
Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
switch (targetSpace) { |
||||
case CONSTRAINT_SPACE_LOCAL: |
||||
return targetObject.getLocalRotation(); |
||||
case CONSTRAINT_SPACE_WORLD: |
||||
return targetObject.getWorldRotation(); |
||||
default: |
||||
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's object scale. |
||||
* @param constraint |
||||
* the constraint instance |
||||
* @return target's object scale |
||||
*/ |
||||
protected Vector3f getTargetScale(Constraint constraint) { |
||||
Long targetOMA = ((Pointer) constraint.getData().getFieldValue("tar")).getOldMemoryAddress(); |
||||
Space targetSpace = constraint.getTargetSpace(); |
||||
Node targetObject = (Node) dataRepository.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE); |
||||
switch (targetSpace) { |
||||
case CONSTRAINT_SPACE_LOCAL: |
||||
return targetObject.getLocalScale(); |
||||
case CONSTRAINT_SPACE_WORLD: |
||||
return targetObject.getWorldScale(); |
||||
default: |
||||
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString()); |
||||
} |
||||
} |
||||
} |
@ -1,162 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.structures; |
||||
|
||||
import com.jme3.animation.BoneAnimation; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
import com.jme3.scene.plugins.blender.utils.DataRepository; |
||||
import com.jme3.scene.plugins.blender.utils.Pointer; |
||||
|
||||
/** |
||||
* The implementation of a constraint. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public class Constraint { |
||||
|
||||
/** The type of this constraint. */ |
||||
private final ConstraintType type; |
||||
/** The name of this constraint. */ |
||||
private final String name; |
||||
/** The old memory address of the constraint's owner. */ |
||||
private final Long boneOMA; |
||||
private final Space ownerSpace; |
||||
private final Space targetSpace; |
||||
/** The structure with constraint's data. */ |
||||
private final Structure data; |
||||
/** The ipo object defining influence. */ |
||||
private final Ipo ipo; |
||||
/** The influence function of this constraint. */ |
||||
private final AbstractInfluenceFunction influenceFunction; |
||||
|
||||
/** |
||||
* This constructor creates the constraint instance. |
||||
* @param constraintStructure |
||||
* the constraint's structure (bConstraint clss in blender 2.49). |
||||
* @param influenceFunction |
||||
* the constraint's influence function (taken from ConstraintHelper) |
||||
* @param boneOMA |
||||
* the old memory address of the constraint owner |
||||
* @param influenceIpo |
||||
* the ipo curve of the influence factor |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
*/ |
||||
public Constraint(Structure constraintStructure, AbstractInfluenceFunction influenceFunction, Long boneOMA, Space ownerSpace, Space targetSpace, Ipo influenceIpo, DataRepository dataRepository) throws BlenderFileException { |
||||
if (influenceFunction == null) { |
||||
throw new IllegalArgumentException("Influence function is not defined!"); |
||||
} |
||||
Pointer pData = (Pointer) constraintStructure.getFieldValue("data"); |
||||
if (pData.isNotNull()) { |
||||
data = pData.fetchData(dataRepository.getInputStream()).get(0); |
||||
} else { |
||||
throw new BlenderFileException("The constraint has no data specified!"); |
||||
} |
||||
this.boneOMA = boneOMA; |
||||
this.type = ConstraintType.valueOf(((Number) constraintStructure.getFieldValue("type")).intValue()); |
||||
this.name = constraintStructure.getFieldValue("name").toString(); |
||||
this.ownerSpace = ownerSpace; |
||||
this.targetSpace = targetSpace; |
||||
this.ipo = influenceIpo; |
||||
this.influenceFunction = influenceFunction; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the name of the constraint. |
||||
* @return the name of the constraint |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the old memoty address of the bone this constraint affects. |
||||
* @return the old memory address of the bone this constraint affects |
||||
*/ |
||||
public Long getBoneOMA() { |
||||
return boneOMA; |
||||
} |
||||
|
||||
/** |
||||
* This method returns owner's transform space. |
||||
* @return owner's transform space |
||||
*/ |
||||
public Space getOwnerSpace() { |
||||
return ownerSpace; |
||||
} |
||||
|
||||
/** |
||||
* This method returns target's transform space. |
||||
* @return target's transform space |
||||
*/ |
||||
public Space getTargetSpace() { |
||||
return targetSpace; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the type of the constraint. |
||||
* @return the type of the constraint |
||||
*/ |
||||
public ConstraintType getType() { |
||||
return type; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the constraint's data structure. |
||||
* @return the constraint's data structure |
||||
*/ |
||||
public Structure getData() { |
||||
return data; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the constraint's influcence curve. |
||||
* @return the constraint's influcence curve |
||||
*/ |
||||
public Ipo getIpo() { |
||||
return ipo; |
||||
} |
||||
|
||||
/** |
||||
* This method affects the bone animation tracks for the given skeleton. |
||||
* @param skeleton |
||||
* the skeleton containing the affected bones by constraint |
||||
* @param boneAnimation |
||||
* the bone animation baked traces |
||||
* @param constraint |
||||
* the constraint |
||||
*/ |
||||
public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation) { |
||||
influenceFunction.affectAnimation(skeleton, boneAnimation, this); |
||||
} |
||||
|
||||
/** |
||||
* The space of target or owner transformation. |
||||
* @author Marcin Roguski |
||||
*/ |
||||
public static enum Space { |
||||
|
||||
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID; |
||||
|
||||
/** |
||||
* This method returns the enum instance when given the appropriate value from the blend file. |
||||
* @param c |
||||
* the blender's value of the space modifier |
||||
* @return the scape enum instance |
||||
*/ |
||||
public static Space valueOf(byte c) { |
||||
switch (c) { |
||||
case 0: |
||||
return CONSTRAINT_SPACE_WORLD; |
||||
case 1: |
||||
return CONSTRAINT_SPACE_LOCAL; |
||||
case 2: |
||||
return CONSTRAINT_SPACE_POSE; |
||||
case 3: |
||||
return CONSTRAINT_SPACE_PARLOCAL; |
||||
default: |
||||
return CONSTRAINT_SPACE_INVALID; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,68 +0,0 @@ |
||||
package com.jme3.scene.plugins.blender.structures; |
||||
|
||||
/** |
||||
* This class represents an object's modifier. The modifier object can be varied and the user needs to know what is |
||||
* the type of it for the specified type name. For example "ArmatureModifierData" type specified in blender is |
||||
* represented by AnimData object from jMonkeyEngine. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
public class Modifier { |
||||
|
||||
public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData"; |
||||
public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData"; |
||||
public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData"; |
||||
public static final String MIRROR_MODIFIER_DATA = "MirrorModifierData"; |
||||
public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData"; |
||||
|
||||
/** |
||||
* Blender's type of modifier. |
||||
*/ |
||||
private String type; |
||||
|
||||
/** |
||||
* JME modifier representation object. |
||||
*/ |
||||
private Object jmeModifierRepresentation; |
||||
|
||||
/** |
||||
* Various additional data used by modifiers. |
||||
*/ |
||||
private Object additionalData; |
||||
|
||||
/** |
||||
* Constructor. Creates modifier object. |
||||
* @param type |
||||
* blender's type of modifier |
||||
* @param modifier |
||||
* JME modifier representation object |
||||
*/ |
||||
public Modifier(String type, Object modifier, Object additionalData) { |
||||
this.type = type; |
||||
this.jmeModifierRepresentation = modifier; |
||||
this.additionalData = additionalData; |
||||
} |
||||
|
||||
/** |
||||
* This method returns JME modifier representation object. |
||||
* @return JME modifier representation object |
||||
*/ |
||||
public Object getJmeModifierRepresentation() { |
||||
return jmeModifierRepresentation; |
||||
} |
||||
|
||||
/** |
||||
* This method returns blender's type of modifier. |
||||
* @return blender's type of modifier |
||||
*/ |
||||
public String getType() { |
||||
return type; |
||||
} |
||||
|
||||
/** |
||||
* This method returns additional data stored in the modifier. |
||||
* @return the additional data stored in the modifier |
||||
*/ |
||||
public Object getAdditionalData() { |
||||
return additionalData; |
||||
} |
||||
} |
@ -1,110 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.scene.plugins.blender.utils; |
||||
|
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
|
||||
/** |
||||
* This interface provides an abstraction to converting loaded blender structures into data structures. The data |
||||
* structures can vary and therefore one can use the loader for different kind of engines. |
||||
* @author Marcin Roguski |
||||
* @param <NodeType> |
||||
* the type of the scene node element |
||||
* @param <CameraType> |
||||
* the type of camera element |
||||
* @param <LightType> |
||||
* the type of light element |
||||
* @param <ObjectType> |
||||
* the type of object element |
||||
* @param <MeshType> |
||||
* the type of mesh element |
||||
* @param <MaterialType> |
||||
* the type of material element |
||||
*/ |
||||
//TODO: ujednolicić wyrzucane wyjątki
|
||||
public interface BlenderConverter<NodeType, CameraType, LightType, ObjectType, MeshType, MaterialType> { |
||||
|
||||
/** |
||||
* This method reads converts the given structure into scene. The given structure needs to be filled with the |
||||
* appropriate data. |
||||
* @param structure |
||||
* the structure we read the scene from |
||||
* @return the scene feature |
||||
*/ |
||||
NodeType toScene(Structure structure); |
||||
|
||||
/** |
||||
* This method reads converts the given structure into camera. The given structure needs to be filled with the |
||||
* appropriate data. |
||||
* @param structure |
||||
* the structure we read the camera from |
||||
* @return the camera feature |
||||
*/ |
||||
CameraType toCamera(Structure structure) throws BlenderFileException; |
||||
|
||||
/** |
||||
* This method reads converts the given structure into light. The given structure needs to be filled with the |
||||
* appropriate data. |
||||
* @param structure |
||||
* the structure we read the light from |
||||
* @return the light feature |
||||
*/ |
||||
LightType toLight(Structure structure) throws BlenderFileException; |
||||
|
||||
/** |
||||
* This method reads converts the given structure into objct. The given structure needs to be filled with the |
||||
* appropriate data. |
||||
* @param structure |
||||
* the structure we read the object from |
||||
* @return the object feature |
||||
*/ |
||||
ObjectType toObject(Structure structure) throws BlenderFileException; |
||||
|
||||
/** |
||||
* This method reads converts the given structure into mesh. The given structure needs to be filled with the |
||||
* appropriate data. |
||||
* @param structure |
||||
* the structure we read the mesh from |
||||
* @return the mesh feature |
||||
*/ |
||||
MeshType toMesh(Structure structure) throws BlenderFileException; |
||||
|
||||
/** |
||||
* This method reads converts the given structure into material. The given structure needs to be filled with the |
||||
* appropriate data. |
||||
* @param structure |
||||
* the structure we read the material from |
||||
* @return the material feature |
||||
*/ |
||||
MaterialType toMaterial(Structure structure) throws BlenderFileException; |
||||
} |
Loading…
Reference in new issue