/// <summary>
        /// Reads a sequence from a stream and assigns it a name.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="seqName">The name to assign.</param>
        /// <returns>The shape.</returns>
        public Shape ImportSequence(Stream stream, string seqName)
        {
            BinaryReader bin = new BinaryReader(stream);
            int readVersion = bin.ReadInt32();
            int exporterVersion = readVersion >> 16;
            readVersion &= 0xFF;
            if (readVersion > Shape.WriteVersion || readVersion < 22)
                return null;

            Shape.ReadVersion = readVersion;
            Assert.Fatal(Shape.ReadVersion >= 22, "TSShapeReader.ImportSequence - Old shape formats are not supported.");

            int[] nodeMap;   // node index of each node from imported sequences
            List<int> checkForDups = new List<int>();

            // Read node names
            // -- this is how we will map imported _sequence nodes to our nodes
            int sz = bin.ReadInt32();
            nodeMap = new int[sz];
            for (int i = 0; i < sz; i++)
            {
                int startSize = _shape.Names.Length;
                int nameIndex = _ReadName(bin, true);
                int count = 0;
                if (nameIndex >= 0)
                {
                    while (checkForDups.Count < nameIndex + 1)
                        checkForDups.Add(0);

                    count = checkForDups[nameIndex]++;
                }

                if (count != 0)
                {
                    // not first time this Name came up...look for later instance of the node
                    nodeMap[i] = -1;
                    for (int j = 0; j < _shape.Nodes.Length; j++)
                    {
                        if (_shape.Nodes[j].NameIndex == nameIndex && count-- == 0)
                        {
                            nodeMap[i] = j;
                            if (j == _shape.Nodes.Length)
                                return null;

                            break;
                        }
                    }
                }
                else
                {
                    nodeMap[i] = _shape.FindNode(nameIndex);
                }

                if (nodeMap[i] < 0)
                {
                    // error -- node found in _sequence but not shape
                    if (_shape.Names.Length != startSize)
                        Assert.Fatal(_shape.Names.Length == startSize, "Shape.ImportSequence - Invalid node in sequence.");

                    return null;
                }
            }

            // Read the following size, but won't do anything with it...legacy:  was going to support
            // import of sequences that Animate objects...we don't...
            sz = bin.ReadInt32();

            // before reading keyframes, take note of a couple numbers
            int oldShapeNumObjects = bin.ReadInt32();

            // adjust all the new keyframes
            int adjNodeRots = _shape.NodeRotations.Length;
            int adjNodeTrans = _shape.NodeTranslations.Length;
            int adjNodeScales1 = _shape.NodeUniformScales.Length;
            int adjNodeScales2 = _shape.NodeAlignedScales.Length;
            int adjNodeScales3 = _shape.NodeArbitraryScaleFactors.Length;
            int adjObjectStates = _shape.ObjectStates.Length - oldShapeNumObjects;
            int adjGroundStates = _shape.GroundTranslations.Length; // groundTrans==groundRot

            // add these node states to our own
            int addNum = bin.ReadInt32();
            _readAndExtendArray(bin, ref _shape.NodeRotations, addNum);
            addNum = bin.ReadInt32();
            _readAndExtendArray(bin, ref _shape.NodeTranslations, addNum);
            addNum = bin.ReadInt32();
            _readAndExtendArray(bin, ref _shape.NodeUniformScales, addNum);
            addNum = bin.ReadInt32();
            _readAndExtendArray(bin, ref _shape.NodeAlignedScales, addNum);
            addNum = bin.ReadInt32();
            _readAndExtendArray(bin, ref _shape.NodeArbitraryScaleRotations, addNum);
            _readAndExtendArray(bin, ref _shape.NodeArbitraryScaleFactors, addNum);
            addNum = bin.ReadInt32();
            _readAndExtendArray(bin, ref _shape.GroundTranslations, addNum);
            _readAndExtendArray(bin, ref _shape.GroundRotations, addNum);

            // add these object states to our own -- shouldn't be any...assume it
            bin.ReadInt32();

            // Read sequences
            sz = bin.ReadInt32();
            int startSeqNum = _shape.Sequences.Length;
            TorqueUtil.ResizeArray<Sequence>(ref _shape.Sequences, startSeqNum + sz);

            for (int i = startSeqNum; i < startSeqNum + sz; i++)
            {
                _shape.Sequences[i] = new Sequence();
                Sequence seq = _shape.Sequences[i];

                // Read Name
                seq.NameIndex = _ReadName(bin, true);

                // Read the rest of the _sequence
                _ReadSequence(bin, ref seq, false);
                seq.BaseRotation += adjNodeRots;
                seq.BaseTranslation += adjNodeTrans;
                if (seq.IsUniformScaleAnimated())
                    seq.BaseScale += adjNodeScales1;
                else if (seq.IsAlignedScaleAnimated())
                    seq.BaseScale += adjNodeScales2;
                else if (seq.IsArbitraryScaleAnimated())
                    seq.BaseScale += adjNodeScales3;

                // not quite so easy...
                // now we have to remap nodes from shape the _sequence came from to this shape
                // that's where nodeMap comes in handy...
                // ditto for the objects.

                // first the nodes
                BitVector newMembership1 = new BitVector();
                BitVector newMembership2 = new BitVector();
                BitVector newMembership3 = new BitVector();
                newMembership1.SetSize(_shape.Nodes.Length);
                newMembership2.SetSize(_shape.Nodes.Length);
                newMembership3.SetSize(_shape.Nodes.Length);
                for (int j = 0; j < nodeMap.Length; j++)
                {
                    if (seq.DoesTranslationMatter.Test(j))
                        newMembership1.Set(nodeMap[j]);

                    if (seq.DoesRotationMatter.Test(j))
                        newMembership2.Set(nodeMap[j]);

                    if (seq.DoesScaleMatter.Test(j))
                        newMembership3.Set(nodeMap[j]);
                }

                seq.DoesTranslationMatter = newMembership1;
                seq.DoesRotationMatter = newMembership2;
                seq.DoesScaleMatter = newMembership3;

                // adjust trigger numbers...we'll Read triggers after sequences...
                seq.FirstTrigger += _shape.Triggers.Length;

                // finally, adjust ground transform's nodes states
                seq.FirstGroundFrame += adjGroundStates;
            }

            // Do we need to rename the last loaded sequence?
            if (seqName != null && _shape.Sequences.Length != 0)
            {
                Sequence seq = _shape.Sequences[_shape.Sequences.Length - 1];

                int nameIndex = _shape.FindName(seqName);
                if (nameIndex < 0)
                {
                    nameIndex = _shape.Names.Length;
                    TorqueUtil.ResizeArray(ref _shape.Names, _shape.Names.Length + 1);
                    _shape.Names[_shape.Names.Length - 1] = seqName;
                }

                seq.NameIndex = nameIndex;
            }

            // add the new triggers
            sz = bin.ReadInt32();
            int startNum = _shape.Triggers.Length;
            TorqueUtil.ResizeArray<Trigger>(ref _shape.Triggers, sz + startNum);
            for (int i = startNum; i < startNum + sz; i++)
            {
                _shape.Triggers[i].State = bin.ReadInt32();
                _shape.Triggers[i].Pos = bin.ReadSingle();
            }

            _shape.Initialize();

            return _shape;
        }
        void _HandleAnimatedScale(Thread thread, int a, int b, ref BitVector scaleBeenSet)
        {
            int j = 0;
            int start = thread.Sequence.DoesScaleMatter.Start();
            int end = b;

            // code the Scale conversion (might need to "upgrade" from uniform to arbitrary, e.g.)
            // code uniform, aligned, and arbitrary as 0,1, and 2, respectively,
            // with _sequence coding in first two bits, shape coding in Next two bits
            int code = 0;
            if (thread.Sequence.IsAlignedScaleAnimated())
                code += 1;
            else if (thread.Sequence.IsArbitraryScaleAnimated())
                code += 2;

            if (AnimatesAlignedScale())
                code += 3;

            if (AnimatesArbitraryScale())
                code += 6;

            float uniformScale = 1.0f;
            Vector3 alignedScale = new Vector3(1, 1, 1);
            ArbitraryScale arbitraryScale = new ArbitraryScale();
            for (int nodeIndex = start; nodeIndex < end; thread.Sequence.DoesScaleMatter.Next(ref nodeIndex), j++)
            {
                if (nodeIndex < a)
                    continue;

                if (!scaleBeenSet.Test(nodeIndex))
                {
                    // compute Scale in _sequence format
                    switch (code)
                    {
                        case 0: // uniform -> uniform
                        case 1: // uniform -> aligned
                        case 2: // uniform -> arbitrary
                            {
                                float s1 = _shape.GetUniformScale(thread.Sequence, thread._keyNum1, j);
                                float s2 = _shape.GetUniformScale(thread.Sequence, thread._keyNum2, j);
                                uniformScale = Transform.Interpolate(s1, s2, thread._keyPos);
                                alignedScale = new Vector3(uniformScale, uniformScale, uniformScale);
                                break;
                            }
                        case 4: // aligned -> aligned
                        case 5: // aligned -> arbitrary
                            {
                                Vector3 s1 = _shape.GetAlignedScale(thread.Sequence, thread._keyNum1, j);
                                Vector3 s2 = _shape.GetAlignedScale(thread.Sequence, thread._keyNum2, j);
                                Transform.Interpolate(s1, s2, thread._keyPos, out alignedScale);
                                break;
                            }
                        case 8: // arbitrary -> arbitary
                            {
                                ArbitraryScale s1, s2;
                                _shape.GetArbitraryScale(thread.Sequence, thread._keyNum1, j, out s1);
                                _shape.GetArbitraryScale(thread.Sequence, thread._keyNum2, j, out s2);
                                Transform.Interpolate(ref s1, ref s2, thread._keyPos, out arbitraryScale);
                                break;
                            }
                        default:
                            Assert.Fatal(false, "ShapeInstance.HandleAnimatedScale - Invalid sequence code.");
                            break;
                    }

                    switch (code)
                    {
                        case 0: // uniform -> uniform
                            {
                                _nodeCurrentUniformScales[nodeIndex] = uniformScale;
                                break;
                            }
                        case 1: // uniform -> aligned
                        case 4: // aligned -> aligned
                            {
                                _nodeCurrentAlignedScales[nodeIndex] = alignedScale;
                                break;
                            }
                        case 2: // uniform -> arbitrary
                        case 5: // aligned -> arbitrary
                            {
                                _nodeCurrentArbitraryScales[nodeIndex].SetIdentity();
                                _nodeCurrentArbitraryScales[nodeIndex].Scale = alignedScale;
                                break;
                            }
                        case 8: // arbitrary -> arbitary
                            {
                                _nodeCurrentArbitraryScales[nodeIndex] = arbitraryScale;
                                break;
                            }
                        default:
                            Assert.Fatal(false, "ShapeInstance.HandleAnimatedScale - Invalid sequence code.");
                            break;
                    }
                    _workScaleThreads[nodeIndex] = thread;
                    scaleBeenSet.Set(nodeIndex);
                }
            }
        }
        void _AnimateNodes(int ss)
        {
            if (_shape.Nodes.Length == 0)
                return;

            // temporary storage for node transforms
            int numNodes = _shape.Nodes.Length;
            if (_nodeCurrentRotations == null || _nodeCurrentRotations.Length < numNodes)
            {
                // grow all these arrays together...no need to check each individually
                TorqueUtil.GrowArray<Quaternion>(ref _nodeCurrentRotations, numNodes);
                TorqueUtil.GrowArray<Vector3>(ref _nodeCurrentTranslations, numNodes);
                TorqueUtil.GrowArray<Thread>(ref _workRotationThreads, numNodes);
                TorqueUtil.GrowArray<Thread>(ref _workTranslationThreads, numNodes);
            }

            BitVector rotBeenSet = new BitVector();
            BitVector tranBeenSet = new BitVector();
            BitVector scaleBeenSet = new BitVector();
            rotBeenSet.SetSize(numNodes);
            rotBeenSet.SetAll();
            tranBeenSet.SetSize(numNodes);
            tranBeenSet.SetAll();
            scaleBeenSet.SetSize(numNodes);
            scaleBeenSet.SetAll();

            int firstBlend = _threadList.Count;
            for (int i = 0; i < _threadList.Count; i++)
            {
                Thread th = _threadList[i];

                if (th.Sequence.IsBlend())
                {
                    // blend sequences need default (if not Set by other _sequence)
                    // break rather than continue because the rest will be blends too
                    firstBlend = i;
                    break;
                }
                rotBeenSet.TakeAway(th.Sequence.DoesRotationMatter);
                tranBeenSet.TakeAway(th.Sequence.DoesTranslationMatter);
                scaleBeenSet.TakeAway(th.Sequence.DoesScaleMatter);
            }

            rotBeenSet.TakeAway(_handsOffNodes);
            tranBeenSet.TakeAway(_handsOffNodes);

            // all the nodes marked above need to have the default transform
            int a = _shape.SubShapeFirstNode[ss];
            int b = a + _shape.SubShapeNodeCount[ss];
            for (int i = a; i < b; i++)
            {
                if (rotBeenSet.Test(i))
                {
                    _shape.DefaultRotations[i].Get(out _nodeCurrentRotations[i]);
                    _workRotationThreads[i] = null;
                }

                if (tranBeenSet.Test(i))
                {
                    _nodeCurrentTranslations[i] = _shape.DefaultTranslations[i];
                    _workTranslationThreads[i] = null;
                }
            }

            // don't want a transform in these cases...
            rotBeenSet.Overlap(_handsOffNodes);
            tranBeenSet.Overlap(_handsOffNodes);

            // default Scale
            if (ScaleCurrentlyAnimated())
                _HandleDefaultScale(a, b, ref scaleBeenSet);

            // handle non-blend sequences
            for (int i = 0; i < firstBlend; i++)
            {
                Thread th = _threadList[i];

                int nodeIndex = th.Sequence.DoesRotationMatter.Start();
                int end = b;
                for (int j = 0; nodeIndex < end; th.Sequence.DoesRotationMatter.Next(ref nodeIndex), j++)
                {
                    // skip nodes outside of this detail
                    if (nodeIndex < a)
                        continue;

                    if (!rotBeenSet.Test(nodeIndex))
                    {
                        Quaternion q1, q2;
                        _shape.GetRotation(th.Sequence, th._keyNum1, j, out q1);
                        _shape.GetRotation(th.Sequence, th._keyNum2, j, out q2);
                        Transform.Interpolate(q1, q2, th._keyPos, out _nodeCurrentRotations[nodeIndex]);
                        rotBeenSet.Set(nodeIndex);
                        _workRotationThreads[nodeIndex] = th;
                    }
                }

                nodeIndex = th.Sequence.DoesTranslationMatter.Start();
                end = b;
                for (int j = 0; nodeIndex < end; th.Sequence.DoesTranslationMatter.Next(ref nodeIndex), j++)
                {
                    if (nodeIndex < a)
                        continue;
                    if (!tranBeenSet.Test(nodeIndex))
                    {
                        Vector3 p1 = _shape.GetTranslation(th.Sequence, th._keyNum1, j);
                        Vector3 p2 = _shape.GetTranslation(th.Sequence, th._keyNum2, j);
                        Transform.Interpolate(p1, p2, th._keyPos, out _nodeCurrentTranslations[nodeIndex]);
                        _workTranslationThreads[nodeIndex] = th;
                        tranBeenSet.Set(nodeIndex);
                    }
                }

                if (ScaleCurrentlyAnimated())
                    _HandleAnimatedScale(th, a, b, ref scaleBeenSet);
            }

            // transitions...
            if (InTransition())
                _HandleTransitionNodes(a, b);

            // compute transforms
            for (int i = a; i < b; i++)
                if (!_handsOffNodes.Test(i))
                    Transform.SetMatrix(_nodeCurrentRotations[i], _nodeCurrentTranslations[i], out _nodeTransforms[i]);

            // add Scale onto transforms
            if (ScaleCurrentlyAnimated())
                _HandleNodeScale(a, b);

            // get callback transforms...
            if (_callbackNodes != null)
                for (int i = 0; i < _callbackNodes.Count; i++)
                    _callbackNodes[i].Transform.GetLocalMatrix(out _nodeTransforms[_callbackNodes[i].NodeIndex], true);

            // handle blend sequences
            for (int i = firstBlend; i < _threadList.Count; i++)
            {
                Thread th = _threadList[i];
                if (th._blendDisabled)
                    continue;

                _HandleBlendSequence(th, a, b);
            }

            // multiply transforms...
            for (int i = a; i < b; i++)
            {
                int parentIdx = _shape.Nodes[i].ParentIndex;
                if (parentIdx >= 0)
                    _nodeTransforms[i] = Matrix.Multiply(_nodeTransforms[i], _nodeTransforms[parentIdx]);
            }
        }
        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 _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);
                    }
                }
            }
        }