Как я могу перемещать кости загруженного актива программно в Away3D?

Я загружаю трехмерный объект в сцену Away3D, и я бы хотел переместить положение костей в код.

Загрузка актива все идет хорошо, я захватываю указатель на Mesh и Skeleton при загрузке:

private function onAssetComplete(evt:AssetEvent):void
{
    if(evt.asset.assetType == AssetType.SKELETON){
        _skeleton = evt.asset as Skeleton;
    } else if (evt.asset.assetType == AssetType.MESH) {
        _mesh = evt.asset as Mesh;
    }
}

После того, как актив завершили загрузку, у меня есть действительный экземпляр Skeleton и Mesh, эта модель также видна в моей сцене. Следующее, что я пробовал, это следующее.

// create a matrix with the desired joint (bone) position
var pos:Matrix3D = new Matrix3D();
pos.position = new Vector3D(60, 0, 0);
pos.invert();

// get the joint I'd like to modifiy. The bone is named "left"
var joint:SkeletonJoint = _skeleton.jointFromName("left");

// assign joint position
joint.inverseBindPose = pos.rawData;

Этот код работает без ошибок, но новая позиция не применяется к видимой геометрии, например. положение кости не изменяется вообще.

Есть ли еще один шаг, который мне не хватает? Должен ли я переписывать скелет в Mesh каким-то образом? Или мне нужно явно указать сетке, что изменилось положение костей?

Ответы

Ответ 1

Это может быть не лучший способ решить эту проблему, но вот что я понял:

Away3D применяет совлокальные преобразования к геометрии при наличии анимации. Чтобы применить ваши преобразования, ваша геометрия должна иметь анимацию, или вам нужно будет создать анимацию в коде. Вот как вы это делаете (желательно в вашем методе обработчика LoaderEvent.RESOURCE_COMPLETE:

// create a new pose for the skeleton
var rootPose:SkeletonPose = new SkeletonPose();

// add all the joints to the pose
// the _skeleton member is being assigned during the loading phase where you
// look for AssetType.SKELETON inside a AssetEvent.ASSET_COMPLETE listener
for each(var joint:SkeletonJoint in _skeleton.joints){
    var m:Matrix3D = new Matrix3D(joint.inverseBindPose);
    m.invert();
    var p:JointPose = new JointPose();
    p.translation = m.transformVector(p.translation);
    p.orientation.fromMatrix(m);
    rootPose.jointPoses.push(p);
}

// create idle animation clip by adding the root pose twice
var clip:SkeletonClipNode = new SkeletonClipNode();
clip.addFrame(rootPose, 1000);
clip.addFrame(rootPose, 1000);
clip.name = "idle";

// build animation set
var animSet:SkeletonAnimationSet = new SkeletonAnimationSet(3);
animSet.addAnimation(clip);

// setup animator with set and skeleton
var animator:SkeletonAnimator = new SkeletonAnimator(animSet, _skeleton);

// assign the newly created animator to your Mesh.
// This example assumes that you grabbed the pointer to _myMesh during the 
// asset loading stage (by looking for AssetType.MESH)
_myMesh.animator = animator;

// run the animation
animator.play("idle");

// it best to keep a member that points to your pose for
// further modification
_myPose = rootPose;

После этого шага инициализации вы можете динамически изменять свои совлокальные позиции (вы изменяете положение, изменяя свойство translation и вращение, изменяя свойство orientation). Пример:

_myPose.jointPoses[2].translation.x = 100;

Если вы не знаете индексы ваших суставов и скорее обращаетесь к костям по имени, это должно работать:

var jointIndex:int = _skeleton.jointIndexFromName("myBoneName");
_myPose.jointPoses[jointIndex].translation.y = 10;

Если вы часто используете поиск имен (скажем, каждый кадр), и у вас много костей в вашей модели, рекомендуется создать Dictionary, где вы можете искать индексы костей по имени. Причиной этого является то, что реализация jointIndexFromName выполняет линейный поиск по всем суставам, который является расточительным, если вы делаете это несколько раз.