public AliceFileBuilder(String inputAliceClassFile = @"Alice\AdultPerson.a3c", int framesPerSegment = 25, int maxSegments = 50, string animationClassName = "KinectAnimation", string outputDirectory = ".") { aliceGenerator = new AliceCodeGenerator(); aliceKinect = new AliceKinect(inputAliceClassFile); // note: it's approx. 10 statements per frame FramesPerSegment = framesPerSegment; MaxSegments = maxSegments; AnimationClassName = animationClassName; OutputDirectory = outputDirectory; }
/// <summary> /// Generates Alice code for rotating a Biped's joints using another object present in the scene. /// The object is placed at each of the skeleton's joints, and then in Alice the parent joint is is pointed at the object to create the desired rotation. /// </summary> /// <param name="skeleton">The Kinect skeleton to calculate joint rotations from</param> /// <returns>A string of Alice code that rotates the joints into their final positions according to the provided Kinect skeleton</returns> public void GetJointsCode(Skeleton skeleton, AliceKinect aliceKinect) { const double twoPi = Math.PI; // compute the final positions for each joint and add the Alice code to the stringbuilder SkeletonPoint fromJoint; SkeletonPoint toJoint; SkeletonPoint adjustedDifference; for (int i = 0; i < boneData.Length; i++) { fromJoint = skeleton.Joints[boneData[i].KinectJointFrom].Position; toJoint = skeleton.Joints[boneData[i].KinectJointTo].Position; // adjust the position difference between the two joints so that it matches the length of the Alice joint // to do this, // (1) get the difference [subtract], // (2) get a unit vector [normalize], and // (3) multiply by the length of the bone adjustedDifference = toJoint .Subtract(fromJoint) .Normalize() .Multiply(boneData[i].JointLength); // the adjusted difference needs to be added to the final position of the 'from' joint, // which is stored in a dictionary and has been computed already if the values in boneData are ordered correctly // ~ it's kind of a dynamic programming approach in that it avoids repeated calculation of the previous joints var finalPosition = finalPositions[boneData[i].KinectJointFrom].Add(adjustedDifference); finalPositions[boneData[i].KinectJointTo] = finalPosition; var rotation = skeleton.BoneOrientations[boneData[i].KinectJointFrom].HierarchicalRotation.Quaternion; var rollAmount = rotation.Y / twoPi; // add the Alice code // (ignore rotations from the neck and pelvis for now, which screw stuff up) if (!ignore.Contains(boneData[i].AliceJointFrom)) { // position the box and point the joint at it aliceKinect.addSetBoxPositionRelativeToVehicle(finalPosition.X, finalPosition.Y, finalPosition.Z); aliceKinect.addPointAt(boneData[i].AliceJointFrom); // roll the bone to match the Kinect data aliceKinect.addRoll(boneData[i].AliceJointFrom, rollAmount, finalPosition.X > 0 ? RollDirection.LEFT : RollDirection.RIGHT); // if the final joint position was behind its parent joint, the parent needs to be rolled 180 degrees (0.5 rotations) // otherwise the joint gets turned around and everything looks weird (twisted spines and knees and such) if (finalPosition.Z > 0 && !rotationIgnore.Contains(boneData[i].AliceJointFrom)) { aliceKinect.addRoll(boneData[i].AliceJointFrom, 0.5, RollDirection.LEFT); } } } }
/// <summary> /// Generates Alice code for moving the entire Biped (allows the Biped to bounce a little during a walking animation, for example). /// </summary> /// <param name="skeleton">The Kinect skeleton to calculate movements from</param> /// <returns>A string of Alice code that executes a Biped's movements</returns> public void GetMovementCode(Skeleton skeleton, AliceKinect aliceKinect) { // get the pelvis var pelvis = skeleton.Joints[JointType.HipCenter].Position; // set the pelvis as the original root if it hasn't been set yet (should happen on the first frame after init() only) if (!originalRootPositionSet) { originalRootPosition = pelvis; originalRootPositionSet = true; } // get the position difference between the original root position and the current position of the root var moveDifference = pelvis.Subtract(originalRootPosition).Normalize().Multiply(0.25f); // this moving algorithm attempts to use absolute positioning rather than relative positioning (adjusting based on the previous position) // in order to stop Alice characters from drifting away after the animation has run a few iterations // 1. move a box that does not have the biped as a vehicle to the desired position aliceKinect.addSetRootPositionRelativeToVehicle(0, moveDifference.Y, 0); // 2. move the person to the box aliceKinect.addMoveToRoot(); }