public Animation(Stream animStream, bool leaveOpen = true, bool alwaysAddBones = false) { Animations = new List <Keyframe>(); // Convert OW Animation to our Animation Type using (BinaryReader animReader = new BinaryReader(animStream, Encoding.Default, leaveOpen)) { Header = animReader.Read <AnimHeader>(); Duration = Header.duration; FramesPerSecond = Header.fps; InfoTableSize = (int)(Header.fps * Header.duration) + 1; ushort bonecount = Header.bonecount; animStream.Seek((long)Header.boneListOffset, SeekOrigin.Begin); for (uint i = 0; i < Header.bonecount; i++) { int boneID = animReader.ReadInt32(); BoneList.Add(boneID); } Vec3d[,] ScaleValues = new Vec3d[bonecount, InfoTableSize]; Vec3d[,] PositionValues = new Vec3d[bonecount, InfoTableSize]; Vec4d[,] RotationValues = new Vec4d[bonecount, InfoTableSize]; bool[,] hasScale = new bool[bonecount, InfoTableSize]; bool[,] hasPosition = new bool[bonecount, InfoTableSize]; bool[,] hasRotation = new bool[bonecount, InfoTableSize]; animStream.Seek((long)Header.infoTableOffset, SeekOrigin.Begin); for (int boneid = 0; boneid < Header.bonecount; boneid++) { long animStreamPos = animStream.Position; AnimInfoTable it = animReader.Read <AnimInfoTable>(); long SIO = (long)it.ScaleIndicesOffset * 4 + animStreamPos; long PIO = (long)it.PositionIndicesOffset * 4 + animStreamPos; long RIO = (long)it.RotationIndicesOffset * 4 + animStreamPos; long SDO = (long)it.ScaleDataOffset * 4 + animStreamPos; long PDO = (long)it.PositionDataOffset * 4 + animStreamPos; long RDO = (long)it.RotationDataOffset * 4 + animStreamPos; InfoTables.Add(it); // Read Indices List <int> ScaleIndexList = new List <int>(); animStream.Seek(SIO, SeekOrigin.Begin); for (int j = 0; j < it.ScaleCount; j++) { if (InfoTableSize <= 255) { ScaleIndexList.Add((int)animReader.ReadByte()); } else { ScaleIndexList.Add((int)animReader.ReadInt16()); } } List <int> PositonIndexList = new List <int>(); animStream.Seek(PIO, SeekOrigin.Begin); for (int j = 0; j < it.PositionCount; j++) { if (InfoTableSize <= 255) { PositonIndexList.Add((int)animReader.ReadByte()); } else { PositonIndexList.Add((int)animReader.ReadInt16()); } } List <int> RotationIndexList = new List <int>(); animStream.Seek(RIO, SeekOrigin.Begin); for (int j = 0; j < it.RotationCount; j++) { if (InfoTableSize <= 255) { RotationIndexList.Add((int)animReader.ReadByte()); } else { RotationIndexList.Add((int)animReader.ReadInt16()); } } // Read Data animStream.Seek(SDO, SeekOrigin.Begin); for (int j = 0; j < it.ScaleCount; j++) { int Index = Math.Abs(ScaleIndexList[j]) % InfoTableSize; hasScale[boneid, Index] = true; ushort x = animReader.ReadUInt16(); ushort y = animReader.ReadUInt16(); ushort z = animReader.ReadUInt16(); Vec3d values = UnpackScale(x, y, z); ScaleValues[boneid, Index] = values; } animStream.Seek(PDO, SeekOrigin.Begin); for (int j = 0; j < it.PositionCount; j++) { int Index = Math.Abs(PositonIndexList[j]) % InfoTableSize; hasPosition[boneid, Index] = true; float x = animReader.ReadSingle(); float y = animReader.ReadSingle(); float z = animReader.ReadSingle(); Vec3d values = new Vec3d(x, y, z); PositionValues[boneid, Index] = values; } animStream.Seek(RDO, SeekOrigin.Begin); for (int j = 0; j < it.RotationCount; j++) { int Index = Math.Abs(RotationIndexList[j]) % InfoTableSize; ushort x = animReader.ReadUInt16(); ushort y = animReader.ReadUInt16(); ushort z = animReader.ReadUInt16(); Vec4d values = UnpackRotation(x, y, z); hasRotation[boneid, Index] = true; RotationValues[boneid, Index] = values; } animStream.Seek(animStreamPos + 32L, SeekOrigin.Begin); } for (int frame = 0; frame < InfoTableSize; frame++) { Keyframe kf = new Keyframe(); kf.FramePosition = ((float)frame / FramesPerSecond); kf.FramePositionI = frame; kf.BoneFrames = new List <BoneAnimation>(); for (int bone = 0; bone < Header.bonecount; bone++) { // Build Value Data BoneAnimation ba = new BoneAnimation(); ba.BoneID = BoneList[bone]; ba.Values = new List <FrameValue>(); if (hasScale[bone, frame]) { Vec3d v = ScaleValues[bone, frame]; FrameValue fv = new FrameValue(AnimChannelID.SCALE, v); ba.Values.Add(fv); } if (hasPosition[bone, frame]) { Vec3d v = PositionValues[bone, frame]; FrameValue f = new FrameValue(AnimChannelID.POSITION, v); ba.Values.Add(f); } if (hasRotation[bone, frame]) { Vec4d v = RotationValues[bone, frame]; FrameValue f = new FrameValue(AnimChannelID.ROTATION, v); ba.Values.Add(f); } if (ba.Values.Count > 0 || alwaysAddBones) { kf.BoneFrames.Add(ba); } } if (kf.BoneFrames.Count > 0) { Animations.Add(kf); } } } }
/** * Add the result of a throw to this frame; if added, null is returned (no new frame is needed): * if we were already at the bottom of the frame, a new frame is created and returned with the throw added. */ public Frame addThrow(FrameValue f) { Frame toReturn = null; // First, ensure the throw is valid; a spare cannot be at the top of the frame, or follow a // strike/spare in the 10th frame. // Similarly, a Strike can only occur as top, or following a Strike / Spare in the tenth frame if (f == FrameValue.Spare) { if (top == null || (frameNumber == 10 && (top == FrameValue.Strike && bottom == null || bottom == FrameValue.Strike))) { throw new InvalidThrowException("Cannot add a spare at the top of a frame."); } } else if (f == FrameValue.Strike) { if ( (frameNumber == 10 && (top != FrameValue.Strike && top != null && (bottom == null || bottom != FrameValue.Spare) ) ) || frameNumber != 10 && top != null && top != FrameValue.Strike && bottom == null ) { throw new InvalidThrowException("Cannot add a strike at the bottom half of a frame (unless 10th and following a strike / spare)."); } } else if (top != null && top != FrameValue.Strike && bottom == null && (getThrowPins(top) + getThrowPins(f) > 10)) { throw new InvalidThrowException("Cannnot add a second value which causes the frame total to exceed 10."); } if (top == null) { top = f; } else if (bottom == null && (top != FrameValue.Strike || frameNumber == 10)) { if (getThrowPins(top) + getThrowPins(f) == 10) { f = FrameValue.Spare; } bottom = f; } else if (frameNumber == 10) { if (tenthBottom == null) { tenthBottom = f; } else { throw new ThrowsExceededException("Cannot add more throws to the 10th frame."); } } else { toReturn = new Frame(this.frameNumber + 1); toReturn.addThrow(f); setNextFrame(toReturn); } return(toReturn); }