@ -42,11 +42,30 @@ import java.util.ArrayList;
* < code > Bone < / code > describes a bone in the bone - weight skeletal animation
* system . A bone contains a name and an index , as well as relevant
* transformation data .
*
* A bone has 3 sets of transforms :
* 1 . The bind transforms , that are the transforms of the bone when the skeleton
* is in its rest pose ( also called bind pose or T pose in the literature ) .
* The bind transforms are expressed in Local space meaning relatively to the
* parent bone .
*
* 2 . The Local transforms , that are the transforms of the bone once animation
* or user transforms has been applied to the bind pose . The local transforms are
* expressed in Local space meaning relatively to the parent bone .
*
* 3 . The Model transforms , that are the transforms of the bone relatives to the
* rootBone of the skeleton . Those transforms are what is needed to apply skinning
* to the mesh the skeleton controls .
* Note that there can be several rootBones in a skeleton . The one considered for
* these transforms is the one that is an ancestor of this bone .
*
* @author Kirill Vainer
* @author Rémy Bouquet
* /
public final class Bone implements Savable {
// Version #2: Changed naming of transforms as they were misleading
public static final int SAVABLE_VERSION = 2 ;
private String name ;
private Bone parent ;
private final ArrayList < Bone > children = new ArrayList < Bone > ( ) ;
@ -59,35 +78,36 @@ public final class Bone implements Savable {
* The attachment node .
* /
private Node attachNode ;
/ * *
* Initial transform is the local bind transform of this bone .
* PARENT SPACE - & gt ; BONE SPACE
* Bind transform is the local bind transform of this bone . ( local space )
* /
private Vector3f initialPos ;
private Quaternion initialRot ;
private Vector3f initialScale ;
private Vector3f bindPos ;
private Quaternion bindRot ;
private Vector3f bindScale ;
/ * *
* The inverse world bind transform .
* BONE SPACE - & gt ; MODEL SPACE
* The inverse bind transforms of this bone expressed in model space
* /
private Vector3f worldBindInversePos ;
private Quaternion worldBindInverseRot ;
private Vector3f worldBindInverseScale ;
private Vector3f modelBindInversePos ;
private Quaternion modelBindInverseRot ;
private Vector3f modelBindInverseScale ;
/ * *
* The local animated transform combined with the local bind transform and parent worl d transform
* The local animated or user transform combined with the local bind transform
* /
private Vector3f localPos = new Vector3f ( ) ;
private Quaternion localRot = new Quaternion ( ) ;
private Vector3f localScale = new Vector3f ( 1 . 0f , 1 . 0f , 1 . 0f ) ;
/ * *
* MODEL SPACE - & gt ; BONE SPACE ( in animated state )
* The model transforms of this bone
* /
private Vector3f world Pos = new Vector3f ( ) ;
private Quaternion world Rot = new Quaternion ( ) ;
private Vector3f world Scale = new Vector3f ( ) ;
private Vector3f model Pos = new Vector3f ( ) ;
private Quaternion model Rot = new Quaternion ( ) ;
private Vector3f model Scale = new Vector3f ( ) ;
// Used for getCombinedTransform
private Transform tmpTransform = new Transform ( ) ;
private Transform tmpTransform ;
/ * *
* Used to handle blending from one animation to another .
@ -107,13 +127,13 @@ public final class Bone implements Savable {
this . name = name ;
initial Pos = new Vector3f ( ) ;
initial Rot = new Quaternion ( ) ;
initial Scale = new Vector3f ( 1 , 1 , 1 ) ;
bind Pos = new Vector3f ( ) ;
bind Rot = new Quaternion ( ) ;
bind Scale = new Vector3f ( 1 , 1 , 1 ) ;
world BindInversePos = new Vector3f ( ) ;
world BindInverseRot = new Quaternion ( ) ;
world BindInverseScale = new Vector3f ( ) ;
model BindInversePos = new Vector3f ( ) ;
model BindInverseRot = new Quaternion ( ) ;
model BindInverseScale = new Vector3f ( ) ;
}
/ * *
@ -135,13 +155,13 @@ public final class Bone implements Savable {
userControl = source . userControl ;
initial Pos = source . initial Pos;
initial Rot = source . initial Rot;
initial Scale = source . initial Scale;
bind Pos = source . bind Pos;
bind Rot = source . bind Rot;
bind Scale = source . bind Scale;
world BindInversePos = source . world BindInversePos;
world BindInverseRot = source . world BindInverseRot;
world BindInverseScale = source . world BindInverseScale;
model BindInversePos = source . model BindInversePos;
model BindInverseRot = source . model BindInverseRot;
model BindInverseScale = source . model BindInverseScale;
// parent and children will be assigned manually..
}
@ -211,7 +231,7 @@ public final class Bone implements Savable {
* @return The position of the bone in model space .
* /
public Vector3f getModelSpacePosition ( ) {
return world Pos;
return model Pos;
}
/ * *
@ -220,7 +240,7 @@ public final class Bone implements Savable {
* @return The rotation of the bone in model space .
* /
public Quaternion getModelSpaceRotation ( ) {
return world Rot;
return model Rot;
}
/ * *
@ -229,79 +249,128 @@ public final class Bone implements Savable {
* @return The scale of the bone in model space .
* /
public Vector3f getModelSpaceScale ( ) {
return world Scale;
return model Scale;
}
/ * *
* Returns the inverse world bind pose position .
/ * *
* @deprecated use { @link # getModelBindInversePosition ( ) }
* /
@Deprecated
public Vector3f getWorldBindInversePosition ( ) {
return modelBindInversePos ;
}
/ * *
* Returns the inverse Bind position of this bone expressed in model space .
* < p >
* The bind pose transform of the bone is its "default"
* The inverse bind pose transform of the bone in model spac e is its "default"
* transform with no animation applied .
*
* @return the inverse world bind pose position .
* /
public Vector3f getWorldBindInversePosition ( ) {
return worldBindInversePos ;
* @return the inverse bind position of this bone expressed in model space .
* /
public Vector3f getModel BindInversePosition ( ) {
return model BindInversePos;
}
/ * *
* Returns the inverse world bind pose rotation .
/ * *
* @deprecated use { @link # getModelBindInverseRotation ( ) }
* /
@Deprecated
public Quaternion getWorldBindInverseRotation ( ) {
return modelBindInverseRot ;
}
/ * *
* Returns the inverse bind rotation of this bone expressed in model space .
* < p >
* The bind pose transform of the bone is its "default"
* The inverse bind pose transform of the bone in model spac e is its "default"
* transform with no animation applied .
*
* @return the inverse world bind pose rotation .
* @return the inverse bind rotation of this bone expressed in model space .
* /
public Quaternion getWorldBindInverseRotation ( ) {
return worldBindInverseRot ;
public Quaternion getModel BindInverseRotation ( ) {
return model BindInverseRot;
}
/ * *
* @deprecated use { @link # getModelBindInverseScale ( ) }
* /
@Deprecated
public Vector3f getWorldBindInverseScale ( ) {
return modelBindInverseScale ;
}
/ * *
* Returns the inverse world bind pose scale .
* < p >
* The bind pose transform of the bone is its "default"
* The inverse bind pose transform of the bone in model spac e is its "default"
* transform with no animation applied .
*
* @return the inverse world bind pose scale .
* /
public Vector3f getWorldBindInverseScale ( ) {
return world BindInverseScale;
public Vector3f getModel BindInverseScale ( ) {
return model BindInverseScale;
}
/ * *
* Returns the world bind pose position .
/ * *
* @deprecated use { @link # getBindPosition ( ) }
* /
@Deprecated
public Vector3f getWorldBindPosition ( ) {
return bindPos ;
}
/ * *
* Returns the bind position expressed in local space ( relative to the parent bone ) .
* < p >
* The bind pose transform of the bone is its "default"
* The bind pose transform of the bone in local space i s its "default"
* transform with no animation applied .
*
* @return the world bind pose position .
* @return the bind position in local space .
* /
public Vector3f getWorldBindPosition ( ) {
return initialPos ;
public Vector3f getBindPosition ( ) {
return bindPos ;
}
/ * *
* @deprecated use { @link # getBindRotation ( ) }
* /
@Deprecated
public Quaternion getWorldBindRotation ( ) {
return bindRot ;
}
/ * *
* Returns the world bind pose rotation .
* Returns the bind rotation expressed in local space ( relative to the parent bone ) .
* < p >
* The bind pose transform of the bone is its "default"
* The bind pose transform of the bone in local space i s its "default"
* transform with no animation applied .
*
* @return the world bind pose rotation .
* @return the bind rotation in local space .
* /
public Quaternion getBindRotation ( ) {
return bindRot ;
}
/ * *
* @deprecated use { @link # getBindScale ( ) }
* /
public Quaternion getWorldBindRotation ( ) {
return initialRot ;
@Deprecated
public Vector3f getWorldBindScale ( ) {
return bindScale ;
}
/ * *
* Returns the world bind pose scale .
* Returns the bind scale expressed in local space ( relative to the parent bone ) .
* < p >
* The bind pose transform of the bone is its "default"
* The bind pose transform of the bone in local space i s its "default"
* transform with no animation applied .
*
* @return the world bind pose scal e.
* @return the bind scale in local spac e.
* /
public Vector3f getWorld BindScale ( ) {
return initial Scale;
public Vector3f getBindScale ( ) {
return bind Scale;
}
/ * *
@ -324,26 +393,36 @@ public final class Bone implements Savable {
}
/ * *
* Updates the world transforms for this bone , and , possibly the attach node
*
* @deprecated use { @link # updateModelTransforms ( ) }
* /
@Deprecated
public final void updateWorldVectors ( ) {
updateModelTransforms ( ) ;
}
/ * *
* Updates the model transforms for this bone , and , possibly the attach node
* if not null .
* < p >
* The world transform of this bone is computed by combining the parent ' s
* world transform with this bones ' local transform .
* The model transform of this bone is computed by combining the parent ' s
* model transform with this bones ' local transform .
* /
public final void updateWorldVectors ( ) {
public final void updateModelTransform s ( ) {
if ( currentWeightSum = = 1f ) {
currentWeightSum = - 1 ;
} else if ( currentWeightSum ! = - 1f ) {
// Apply the weight to the local transform
if ( currentWeightSum = = 0 ) {
localRot . set ( initial Rot) ;
localPos . set ( initial Pos) ;
localScale . set ( initial Scale) ;
localRot . set ( bind Rot) ;
localPos . set ( bind Pos) ;
localScale . set ( bind Scale) ;
} else {
float invWeightSum = 1f - currentWeightSum ;
localRot . nlerp ( initial Rot, invWeightSum ) ;
localPos . interpolateLocal ( initial Pos, invWeightSum ) ;
localScale . interpolateLocal ( initial Scale, invWeightSum ) ;
localRot . nlerp ( bind Rot, invWeightSum ) ;
localPos . interpolateLocal ( bind Pos, invWeightSum ) ;
localScale . interpolateLocal ( bind Scale, invWeightSum ) ;
}
// Future invocations of transform blend will start over.
@ -352,28 +431,28 @@ public final class Bone implements Savable {
if ( parent ! = null ) {
//rotation
parent . world Rot. mult ( localRot , world Rot) ;
parent . model Rot. mult ( localRot , model Rot) ;
//scale
//For scale parent scale is not taken into account!
// worldScale.set(localScale);
parent . world Scale. mult ( localScale , world Scale) ;
parent . model Scale. mult ( localScale , model Scale) ;
//translation
//scale and rotation of parent affect bone position
parent . world Rot. mult ( localPos , world Pos) ;
world Pos. multLocal ( parent . world Scale) ;
world Pos. addLocal ( parent . world Pos) ;
parent . model Rot. mult ( localPos , model Pos) ;
model Pos. multLocal ( parent . model Scale) ;
model Pos. addLocal ( parent . model Pos) ;
} else {
world Rot. set ( localRot ) ;
world Pos. set ( localPos ) ;
world Scale. set ( localScale ) ;
model Rot. set ( localRot ) ;
model Pos. set ( localPos ) ;
model Scale. set ( localScale ) ;
}
if ( attachNode ! = null ) {
attachNode . setLocalTranslation ( world Pos) ;
attachNode . setLocalRotation ( world Rot) ;
attachNode . setLocalScale ( world Scale) ;
attachNode . setLocalTranslation ( model Pos) ;
attachNode . setLocalRotation ( model Rot) ;
attachNode . setLocalScale ( model Scale) ;
}
}
@ -381,7 +460,7 @@ public final class Bone implements Savable {
* Updates world transforms for this bone and it ' s children .
* /
final void update ( ) {
this . updateWorldVector s ( ) ;
this . updateModelTransform s ( ) ;
for ( int i = children . size ( ) - 1 ; i > = 0 ; i - - ) {
children . get ( i ) . update ( ) ;
@ -392,25 +471,25 @@ public final class Bone implements Savable {
* Saves the current bone state as its binding pose , including its children .
* /
void setBindingPose ( ) {
initial Pos. set ( localPos ) ;
initial Rot. set ( localRot ) ;
initial Scale. set ( localScale ) ;
if ( world BindInversePos = = null ) {
world BindInversePos = new Vector3f ( ) ;
world BindInverseRot = new Quaternion ( ) ;
world BindInverseScale = new Vector3f ( ) ;
bind Pos. set ( localPos ) ;
bind Rot. set ( localRot ) ;
bind Scale. set ( localScale ) ;
if ( model BindInversePos = = null ) {
model BindInversePos = new Vector3f ( ) ;
model BindInverseRot = new Quaternion ( ) ;
model BindInverseScale = new Vector3f ( ) ;
}
// Save inverse derived position/scale/orientation, used for calculate offset transform later
world BindInversePos. set ( world Pos) ;
world BindInversePos. negateLocal ( ) ;
model BindInversePos. set ( model Pos) ;
model BindInversePos. negateLocal ( ) ;
world BindInverseRot. set ( world Rot) ;
world BindInverseRot. inverseLocal ( ) ;
model BindInverseRot. set ( model Rot) ;
model BindInverseRot. inverseLocal ( ) ;
world BindInverseScale. set ( Vector3f . UNIT_XYZ ) ;
world BindInverseScale. divideLocal ( world Scale) ;
model BindInverseScale. set ( Vector3f . UNIT_XYZ ) ;
model BindInverseScale. divideLocal ( model Scale) ;
for ( Bone b : children ) {
b . setBindingPose ( ) ;
@ -422,9 +501,9 @@ public final class Bone implements Savable {
* /
final void reset ( ) {
if ( ! userControl ) {
localPos . set ( initial Pos) ;
localRot . set ( initial Rot) ;
localScale . set ( initial Scale) ;
localPos . set ( bind Pos) ;
localRot . set ( bind Rot) ;
localScale . set ( bind Scale) ;
}
for ( int i = children . size ( ) - 1 ; i > = 0 ; i - - ) {
@ -444,14 +523,14 @@ public final class Bone implements Savable {
* /
void getOffsetTransform ( Matrix4f outTransform , Quaternion tmp1 , Vector3f tmp2 , Vector3f tmp3 , Matrix3f tmp4 ) {
// Computing scale
Vector3f scale = world Scale. mult ( world BindInverseScale, tmp3 ) ;
Vector3f scale = model Scale. mult ( model BindInverseScale, tmp3 ) ;
// Computing rotation
Quaternion rotate = world Rot. mult ( world BindInverseRot, tmp1 ) ;
Quaternion rotate = model Rot. mult ( model BindInverseRot, tmp1 ) ;
// Computing translation
// Translation depend on rotation and scale
Vector3f translate = world Pos. add ( rotate . mult ( scale . mult ( world BindInversePos, tmp2 ) , tmp2 ) , tmp2 ) ;
Vector3f translate = model Pos. add ( rotate . mult ( scale . mult ( model BindInversePos, tmp2 ) , tmp2 ) , tmp2 ) ;
// Populating the matrix
outTransform . loadIdentity ( ) ;
@ -459,35 +538,52 @@ public final class Bone implements Savable {
}
/ * *
* Sets user transform .
*
* Sets the transforms of this bone in local space ( relative to the parent bone )
*
* @param translation the translation in local space
* @param rotation the rotation in local space
* @param scale the scale in local space
* /
public void setUserTransforms ( Vector3f translation , Quaternion rotation , Vector3f scale ) {
if ( ! userControl ) {
throw new IllegalStateException ( "User control must be on bone to allow user transforms" ) ;
throw new IllegalStateException ( "You must call setUserControl(true) in order to setUserTransform to work " ) ;
}
localPos . set ( initial Pos) ;
localRot . set ( initial Rot) ;
localScale . set ( initial Scale) ;
localPos . set ( bind Pos) ;
localRot . set ( bind Rot) ;
localScale . set ( bind Scale) ;
localPos . addLocal ( translation ) ;
localRot = localRot . mult ( rotation ) ;
localRot . multLocal ( rotation ) ;
localScale . multLocal ( scale ) ;
}
/ * *
* Must update all bones in skeleton for this to work .
* @param translation
* @param rotation
*
* @param translation -
* @param rotation -
* @deprecated use { @link # setUserTransformsInModelSpace ( com . jme3 . math . Vector3f , com . jme3 . math . Quaternion ) }
* /
@Deprecated
public void setUserTransformsWorld ( Vector3f translation , Quaternion rotation ) {
}
/ * *
* Sets the transforms of this bone in model space ( relative to the root bone )
*
* Must update all bones in skeleton for this to work .
* @param translation translation in model space
* @param rotation rotation in model space
* /
public void setUserTransformsInModelSpace ( Vector3f translation , Quaternion rotation ) {
if ( ! userControl ) {
throw new IllegalStateException ( "User control must be on bone to allow user transforms" ) ;
throw new IllegalStateException ( "You must call setUserControl(true) in order to setUserTransformsInModelSpace to work " ) ;
}
// TODO: add scale here ???
worldPos . set ( translation ) ;
worldRot . set ( rotation ) ;
model Pos. set ( translation ) ;
model Rot. set ( rotation ) ;
//if there is an attached Node we need to set it's local transforms too.
if ( attachNode ! = null ) {
@ -502,6 +598,9 @@ public final class Bone implements Savable {
* @param rotation a rotation
* /
public Transform getCombinedTransform ( Vector3f position , Quaternion rotation ) {
if ( tmpTransform = = null ) {
tmpTransform = new Transform ( ) ;
}
rotation . mult ( localPos , tmpTransform . getTranslation ( ) ) . addLocal ( position ) ;
tmpTransform . setRotation ( rotation ) . getRotation ( ) . multLocal ( localRot ) ;
return tmpTransform ;
@ -541,11 +640,11 @@ public final class Bone implements Savable {
// localRot.multLocal(rotation);
//localRot = localRot.mult(rotation);
localPos . set ( initial Pos) . addLocal ( translation ) ;
localRot . set ( initial Rot) . multLocal ( rotation ) ;
localPos . set ( bind Pos) . addLocal ( translation ) ;
localRot . set ( bind Rot) . multLocal ( rotation ) ;
if ( scale ! = null ) {
localScale . set ( initial Scale) . multLocal ( scale ) ;
localScale . set ( bind Scale) . multLocal ( scale ) ;
}
}
@ -553,11 +652,11 @@ public final class Bone implements Savable {
* Blends the given animation transform onto the bone ' s local transform .
* < p >
* Subsequent calls of this method stack up , with the final transformation
* of the bone computed at { @link # updateWorldVector s ( ) } which resets
* of the bone computed at { @link # updateModelTransform s ( ) } which resets
* the stack .
* < p >
* E . g . a single transform blend with weight = 0 . 5 followed by an
* updateWorldVector s ( ) call will result in final transform = transform * 0 . 5 .
* updateModelTransform s ( ) call will result in final transform = transform * 0 . 5 .
* Two transform blends with weight = 0 . 5 each will result in the two
* transforms blended together ( nlerp ) with blend = 0 . 5 .
*
@ -565,7 +664,7 @@ public final class Bone implements Savable {
* @param rotation The rotation to blend in
* @param scale The scale to blend in
* @param weight The weight of the transform to apply . Set to 1 . 0 to prevent
* any other transform from being applied until updateWorldVector s ( ) .
* any other transform from being applied until updateModelTransform s ( ) .
* /
void blendAnimTransforms ( Vector3f translation , Quaternion rotation , Vector3f scale , float weight ) {
if ( userControl ) {
@ -581,12 +680,12 @@ public final class Bone implements Savable {
return ; // More than 2 transforms are being blended
} else if ( currentWeightSum = = - 1 | | currentWeightSum = = 0 ) {
// Set the transform fully
localPos . set ( initial Pos) . addLocal ( translation ) ;
localRot . set ( initial Rot) . multLocal ( rotation ) ;
localPos . set ( bind Pos) . addLocal ( translation ) ;
localRot . set ( bind Rot) . multLocal ( rotation ) ;
if ( scale ! = null ) {
localScale . set ( initial Scale) . multLocal ( scale ) ;
localScale . set ( bind Scale) . multLocal ( scale ) ;
}
// Set the weight. It will be applied in updateWorldVector s().
// Set the weight. It will be applied in updateModelTransform s().
currentWeightSum = weight ;
} else {
// The weight is already set.
@ -597,14 +696,14 @@ public final class Bone implements Savable {
Vector3f tmpV2 = vars . vect2 ;
Quaternion tmpQ = vars . quat1 ;
tmpV . set ( initial Pos) . addLocal ( translation ) ;
tmpV . set ( bind Pos) . addLocal ( translation ) ;
localPos . interpolateLocal ( tmpV , weight ) ;
tmpQ . set ( initial Rot) . multLocal ( rotation ) ;
tmpQ . set ( bind Rot) . multLocal ( rotation ) ;
localRot . nlerp ( tmpQ , weight ) ;
if ( scale ! = null ) {
tmpV2 . set ( initial Scale) . multLocal ( scale ) ;
tmpV2 . set ( bind Scale) . multLocal ( scale ) ;
localScale . interpolateLocal ( tmpV2 , weight ) ;
}
@ -620,11 +719,11 @@ public final class Bone implements Savable {
* Call setBindingPose ( ) after all of the skeleton bones ' bind transforms are set to save them .
* /
public void setBindTransforms ( Vector3f translation , Quaternion rotation , Vector3f scale ) {
initial Pos. set ( translation ) ;
initial Rot. set ( rotation ) ;
bind Pos. set ( translation ) ;
bind Rot. set ( rotation ) ;
//ogre.xml can have null scale values breaking this if the check is removed
if ( scale ! = null ) {
initial Scale. set ( scale ) ;
bind Scale. set ( scale ) ;
}
localPos . set ( translation ) ;
@ -658,13 +757,22 @@ public final class Bone implements Savable {
InputCapsule input = im . getCapsule ( this ) ;
name = input . readString ( "name" , null ) ;
initialPos = ( Vector3f ) input . readSavable ( "initialPos" , null ) ;
initialRot = ( Quaternion ) input . readSavable ( "initialRot" , null ) ;
initialScale = ( Vector3f ) input . readSavable ( "initialScale" , new Vector3f ( 1 . 0f , 1 . 0f , 1 . 0f ) ) ;
int ver = input . getSavableVersion ( Bone . class ) ;
if ( ver < 2 ) {
bindPos = ( Vector3f ) input . readSavable ( "initialPos" , null ) ;
bindRot = ( Quaternion ) input . readSavable ( "initialRot" , null ) ;
bindScale = ( Vector3f ) input . readSavable ( "initialScale" , new Vector3f ( 1 . 0f , 1 . 0f , 1 . 0f ) ) ;
} else {
bindPos = ( Vector3f ) input . readSavable ( "bindPos" , null ) ;
bindRot = ( Quaternion ) input . readSavable ( "bindRot" , null ) ;
bindScale = ( Vector3f ) input . readSavable ( "bindScale" , new Vector3f ( 1 . 0f , 1 . 0f , 1 . 0f ) ) ;
}
attachNode = ( Node ) input . readSavable ( "attachNode" , null ) ;
localPos . set ( initialPos ) ;
localRot . set ( initialRot ) ;
localPos . set ( bindPos ) ;
localRot . set ( bindRot ) ;
localScale . set ( bindScale ) ;
ArrayList < Bone > childList = input . readSavableArrayList ( "children" , null ) ;
for ( int i = childList . size ( ) - 1 ; i > = 0 ; i - - ) {
@ -683,9 +791,9 @@ public final class Bone implements Savable {
output . write ( name , "name" , null ) ;
output . write ( attachNode , "attachNode" , null ) ;
output . write ( initialPos , "initial Pos", null ) ;
output . write ( initialRot , "initial Rot", null ) ;
output . write ( initialScale , "initial Scale", new Vector3f ( 1 . 0f , 1 . 0f , 1 . 0f ) ) ;
output . write ( bindPos , "bind Pos", null ) ;
output . write ( bindRot , "bind Rot", null ) ;
output . write ( bindScale , "bind Scale", new Vector3f ( 1 . 0f , 1 . 0f , 1 . 0f ) ) ;
output . writeSavableArrayList ( children , "children" , null ) ;
}
}