void _AnimateVisibility(int ss)
        {
            if (_meshObjects.Length == 0)
                return;

            // find out who needs default values Set
            BitVector beenSet = new BitVector();
            beenSet.SetSize(_meshObjects.Length);
            beenSet.SetAll();
            for (int i = 0; i < _threadList.Count; i++)
                beenSet.TakeAway(_threadList[i].Sequence.DoesVisibilityMatter);

            // Set defaults
            int a = _shape.SubShapeFirstObject[ss];
            int b = a + _shape.SubShapeObjectCount[ss];
            for (int i = a; i < b; i++)
            {
                if (beenSet.Test(i))
                    _meshObjects[i].Visibility = _shape.ObjectStates[i].Visibility;
            }

            // go through each thread and Set visibility on those objects that
            // are not Set yet and are controlled by that thread
            BitVector objectMatters = new BitVector();
            for (int i = 0; i < _threadList.Count; i++)
            {
                Thread th = _threadList[i];

                objectMatters.Copy(ref th.Sequence.DoesFrameMatter);
                objectMatters.Overlap(th.Sequence.DoesMaterialFrameMatter);
                objectMatters.Overlap(th.Sequence.DoesVisibilityMatter);

                // skip to beginining of this sub-shape
                int j = 0;
                int start = objectMatters.Start();
                int end = b;
                for (int objectIndex = start; objectIndex < b; objectMatters.Next(ref objectIndex), j++)
                {
                    if (!beenSet.Test(objectIndex) && th.Sequence.DoesVisibilityMatter.Test(objectIndex))
                    {
                        float state1 = _shape.GetObjectState(th.Sequence, th._keyNum1, j).Visibility;
                        float state2 = _shape.GetObjectState(th.Sequence, th._keyNum2, j).Visibility;
                        if ((state1 - state2) * (state1 - state2) > 0.99f)
                            // goes from 0 to 1 -- discreet jump
                            _meshObjects[objectIndex].Visibility = th._keyPos < 0.5f ? state1 : state2;
                        else
                            // Interpolate between keyframes when visibility change is gradual
                            _meshObjects[objectIndex].Visibility = (1.0f - th._keyPos) * state1 + th._keyPos * state2;

                        // record change so that later threads don't over-write us...
                        beenSet.Set(objectIndex);
                    }
                }
            }
        }
        void _HandleBlendSequence(Thread thread, int a, int b)
        {
            int jrot = 0;
            int jtrans = 0;
            int jscale = 0;

            BitVector nodeMatters = new BitVector();
            nodeMatters.Copy(ref thread.Sequence.DoesTranslationMatter);
            nodeMatters.Overlap(thread.Sequence.DoesRotationMatter);
            nodeMatters.Overlap(thread.Sequence.DoesScaleMatter);
            int start = nodeMatters.Start();
            int end = b;
            for (int nodeIndex = start; nodeIndex < end; nodeMatters.Next(ref nodeIndex))
            {
                // skip nodes outside of this detail
                if (start < a || _disableBlendNodes.Test(nodeIndex))
                {
                    if (thread.Sequence.DoesRotationMatter.Test(nodeIndex))
                        jrot++;

                    if (thread.Sequence.DoesTranslationMatter.Test(nodeIndex))
                        jtrans++;

                    if (thread.Sequence.DoesScaleMatter.Test(nodeIndex))
                        jscale++;

                    continue;
                }

                Matrix mat = Matrix.Identity;

                if (thread.Sequence.DoesRotationMatter.Test(nodeIndex))
                {
                    Quaternion q1, q2;
                    _shape.GetRotation(thread.Sequence, thread._keyNum1, jrot, out q1);
                    _shape.GetRotation(thread.Sequence, thread._keyNum2, jrot, out q2);
                    Quaternion quat;
                    Transform.Interpolate(q1, q2, thread._keyPos, out quat);
                    Transform.SetMatrix(quat, out mat);
                    jrot++;
                }

                if (thread.Sequence.DoesTranslationMatter.Test(nodeIndex))
                {
                    Vector3 p1 = _shape.GetTranslation(thread.Sequence, thread._keyNum1, jtrans);
                    Vector3 p2 = _shape.GetTranslation(thread.Sequence, thread._keyNum2, jtrans);
                    Vector3 p;
                    Transform.Interpolate(p1, p2, thread._keyPos, out p);
                    mat.Translation = p;
                    jtrans++;
                }

                if (thread.Sequence.DoesScaleMatter.Test(nodeIndex))
                {
                    if (thread.Sequence.IsUniformScaleAnimated())
                    {
                        float s1 = _shape.GetUniformScale(thread.Sequence, thread._keyNum1, jscale);
                        float s2 = _shape.GetUniformScale(thread.Sequence, thread._keyNum2, jscale);
                        float scale = Transform.Interpolate(s1, s2, thread._keyPos);
                        Transform.ApplyScale(scale, ref mat);
                    }
                    else if (AnimatesAlignedScale())
                    {
                        Vector3 s1 = _shape.GetAlignedScale(thread.Sequence, thread._keyNum1, jscale);
                        Vector3 s2 = _shape.GetAlignedScale(thread.Sequence, thread._keyNum2, jscale);
                        Vector3 scale;
                        Transform.Interpolate(s1, s2, thread._keyPos, out scale);
                        Transform.ApplyScale(scale, ref mat);
                    }
                    else
                    {
                        ArbitraryScale s1, s2;
                        _shape.GetArbitraryScale(thread.Sequence, thread._keyNum1, jscale, out s1);
                        _shape.GetArbitraryScale(thread.Sequence, thread._keyNum2, jscale, out s2);
                        ArbitraryScale scale;
                        Transform.Interpolate(ref s1, ref s2, thread._keyPos, out scale);
                        Transform.ApplyScale(scale, ref mat);
                    }
                    jscale++;
                }

                // apply blend transform
                _nodeTransforms[nodeIndex] = Matrix.Multiply(mat, _nodeTransforms[nodeIndex]);
            }
        }
        void _AnimateMatFrame(int ss)
        {
            if (_meshObjects.Length == 0)
                return;

            // find out who needs default values Set
            BitVector beenSet = new BitVector();
            beenSet.SetSize(_meshObjects.Length);
            beenSet.SetAll();
            for (int i = 0; i < _threadList.Count; i++)
                beenSet.TakeAway(_threadList[i].Sequence.DoesMaterialFrameMatter);

            // Set defaults
            int a = _shape.SubShapeFirstObject[ss];
            int b = a + _shape.SubShapeObjectCount[ss];
            for (int i = a; i < b; i++)
            {
                if (beenSet.Test(i))
                    _meshObjects[i].MaterialFrame = _shape.ObjectStates[i].MaterialFrameIndex;
            }

            // go through each thread and Set matFrame on those objects that
            // are not Set yet and are controlled by that thread
            BitVector objectMatters = new BitVector();
            for (int i = 0; i < _threadList.Count; i++)
            {
                Thread th = _threadList[i];

                objectMatters.Copy(ref th.Sequence.DoesFrameMatter);
                objectMatters.Overlap(th.Sequence.DoesMaterialFrameMatter);
                objectMatters.Overlap(th.Sequence.DoesVisibilityMatter);

                // skip to beginining of this sub-shape
                int j = 0;
                int start = objectMatters.Start();
                int end = b;
                for (int objectIndex = start; objectIndex < end; objectMatters.Next(ref objectIndex), j++)
                {
                    if (!beenSet.Test(objectIndex) && th.Sequence.DoesMaterialFrameMatter.Test(objectIndex))
                    {
                        int key = (th._keyPos < 0.5f) ? th._keyNum1 : th._keyNum2;
                        _meshObjects[objectIndex].MaterialFrame = _shape.GetObjectState(th.Sequence, key, j).MaterialFrameIndex;

                        // record change so that later threads don't over-write us...
                        beenSet.Set(objectIndex);
                    }
                }
            }
        }