Exemplo n.º 1
0
        public static void GenerateAnimCommandsTransform(SkeletalModel model)
        {
            if(EngineWorld.AnimCommands.Length == 0)
            {
                return;
            }
            //Sys.DebugLog("anim_transform.txt", "MODEL[{0}]", model.ID);
            for (var anim = 0; anim < model.Animations.Count; anim++)
            {
                if(model.Animations[anim].NumAnimCommands > 255)
                {
                    continue; // If no anim commands or current anim has more than 255 (according to TRosettaStone).
                }

                var af = model.Animations[anim];
                if (af.NumAnimCommands == 0)
                    continue;

                Assert(af.AnimCommand < EngineWorld.AnimCommands.Length);
                var ac = (int) af.AnimCommand;

                for (var i = 0; i < af.NumAnimCommands; i++)
                {
                    var command = EngineWorld.AnimCommands[ac];
                    ac++;
                    switch((TR_ANIMCOMMAND)command)
                    {
                        case TR_ANIMCOMMAND.SetPosition:
                            // This command executes ONLY at the end of animation.
                            af.Frames.Last().Move.X = EngineWorld.AnimCommands[ac + 0]; // x = x
                            af.Frames.Last().Move.Z = -EngineWorld.AnimCommands[ac + 1]; // z = -y
                            af.Frames.Last().Move.Y = EngineWorld.AnimCommands[ac + 2]; // y = z
                            af.Frames.Last().Command |= (ushort)ANIM_CMD.Move;
                            ac += 3;
                            break;

                        case TR_ANIMCOMMAND.JumpDistance:
                            af.Frames.Last().V_Vertical= EngineWorld.AnimCommands[ac + 0];
                            af.Frames.Last().V_Horizontal = -EngineWorld.AnimCommands[ac + 1];
                            af.Frames.Last().Command |= (ushort) ANIM_CMD.Jump;
                            ac += 2;
                            break;

                        case TR_ANIMCOMMAND.EmptyHands:
                            break;

                        case TR_ANIMCOMMAND.Kill:
                            break;

                        case TR_ANIMCOMMAND.PlaySound:
                            ac += 2;
                            break;

                        case TR_ANIMCOMMAND.PlayEffect:
                            switch(EngineWorld.AnimCommands[ac + 1] & 0x3FFF)
                            {
                                case (int)TR_EFFECT.ChangeDirection:
                                    af.Frames.Last().Command |= (ushort) ANIM_CMD.ChangeDirection;
                                    ConsoleInfo.Instance.Printf("ROTATE: anim = {0}, frame = {1} of {2}", anim, EngineWorld.AnimCommands[ac + 0], af.Frames.Count);
                                    break;
                            }
                            ac += 2;
                            break;
                    }
                }
            }
        }
Exemplo n.º 2
0
        public static void TR_GenSkeletalModel(World world, int model_num, SkeletalModel model, Level tr)
        {
            var trMoveable = tr.Moveables[model_num]; // original tr structure

            model.CollisionMap.Resize(model.MeshCount);
            for (ushort i = 0; i < model.MeshCount; i++)
            {
                model.CollisionMap[i] = i;
            }

            model.MeshTree.Resize(model.MeshCount, () => new MeshTreeTag());
            var treeTag = model.MeshTree[0];

            var meshIndex = tr.MeshIndices.SkipEx(trMoveable.StartingMesh);

            for (var k = 0; k < model.MeshCount; k++)
            {
                treeTag = model.MeshTree[k];
                treeTag.MeshBase = world.Meshes[(int) meshIndex[k]];
                treeTag.MeshSkin = null; // PARANOID: I use calloc for tree_tag's
                treeTag.ReplaceAnim = 0x00;
                treeTag.ReplaceMesh = 0x00;
                treeTag.BodyPart = 0x00;
                treeTag.Offset = Vector3.Zero;
                if (k == 0)
                {
                    treeTag.Flag = 0x02;
                }
                else
                {
                    var tr_mesh_tree = tr.MeshTreeData.SkipEx((int) trMoveable.MeshTreeIndex + (k - 1) * 4);
                    treeTag.Flag = (ushort) (tr_mesh_tree[0] & 0xFF);
                    treeTag.Offset.X = tr_mesh_tree[1];
                    treeTag.Offset.Y = tr_mesh_tree[3];
                    treeTag.Offset.Z = -tr_mesh_tree[2];
                }
            }

            /*
             * =================    now, animation loading    ========================
             */

            if (trMoveable.AnimationIndex >= tr.Animations.Length)
            {
                // model has no start offset and any animation
                model.Animations.Resize(1, () => new AnimationFrame());
                model.Animations[0].Frames.Resize(1, () => new BoneFrame());
                var boneFrame = model.Animations[0].Frames[0];

                model.Animations[0].ID = TR_ANIMATION.LaraRun;
                model.Animations[0].NextAnim = null;
                model.Animations[0].NextFrame = 0;
                model.Animations[0].StateChange.Clear();
                model.Animations[0].OriginalFrameRate = 1;

                boneFrame.BoneTags.Resize(model.MeshCount, () => new BoneTag());

                boneFrame.Position = Vector3.Zero;
                boneFrame.Move = Vector3.Zero;
                boneFrame.V_Horizontal = 0.0f;
                boneFrame.V_Vertical = 0.0f;
                boneFrame.Command = 0x00;
                for (var k = 0; k < boneFrame.BoneTags.Count; k++)
                {
                    treeTag = model.MeshTree[k];
                    var boneTag = boneFrame.BoneTags[k];

                    VMath.Vec4_SetTRRotations(ref boneTag.QRotate, Vector3.Zero);
                    boneTag.Offset = treeTag.Offset;
                }
                return;
            }
            //Sys.DebugLog(LOG_FILENAME, "model = {0}, anims = {1}", trMoveable.ObjectID, TR_GetNumAnimationsForMoveable(tr, model_num));
            model.Animations.Resize(Math.Max(1, TR_GetNumAnimationsForMoveable(tr, model_num)),
                () => new AnimationFrame()); // the animation count must be >= 1

            /*
             *   Ok, let us calculate animations;
             *   there is no difficult:
             * - first 9 words are bounding box and frame offset coordinates.
             * - 10's word is a rotations count, must be equal to number of meshes in model.
             *   BUT! only in TR1. In TR2 - TR5 after first 9 words begins next section.
             * - in the next follows rotation's data. one word - one rotation, if rotation is one-axis (one angle).
             *   two words in 3-axis rotations (3 angles). angles are calculated with bit mask.
             */
            for (var i = 0; i < model.Animations.Count; i++)
            {
                var anim = model.Animations[i];
                var trAnimation = tr.Animations[trMoveable.AnimationIndex + i];

                var frameOffset = trAnimation.FrameOffset / 2;
                var l_start = 0x09;

                if (tr.GameVersion.IsAnyOf(TRGame.TR1, TRGame.TR1Demo, TRGame.TR1UnfinishedBusiness))
                {
                    l_start = 0x0A;
                }

                var frameStep = trAnimation.FrameSize;

                anim.ID = (TR_ANIMATION) i;
                anim.OriginalFrameRate = trAnimation.FrameRate;

                anim.SpeedX = trAnimation.Speed;
                anim.AccelX = trAnimation.Acceleration;
                anim.SpeedY = trAnimation.AccelerationLateral;
                anim.AccelY = trAnimation.SpeedLateral; // TODO: Inverted?

                anim.AnimCommand = trAnimation.AnimCommand;
                anim.NumAnimCommands = trAnimation.NumAnimCommands;
                anim.StateID = (TR_STATE) trAnimation.StateID;

                anim.Frames.Resize(TR_GetNumFramesForAnimation(tr, trMoveable.AnimationIndex + i), () => new BoneFrame());

                //Sys.DebugLog(LOG_FILENAME, "Anim[{0}], {1}", trMoveable.AnimationIndex, TR_GetNumFramesForAnimation(tr, trMoveable.AnimationIndex));

                // Parse AnimCommands
                // Max. amount of AnimCommands is 255, larger numbers are considered as 0.
                // See http://evpopov.com/dl/TR4format.html#Animations for details.

                if (anim.NumAnimCommands.IsBetween(0, 255, IB.aEbI))
                {
                    // Calculate current animation anim command block offset.
                    Assert(anim.AnimCommand < world.AnimCommands.Length);
                    unsafe
                    {
                        fixed (short* tmp = &world.AnimCommands[(int) anim.AnimCommand])
                        {
                            var pointer = tmp;
                            for (uint count = 0; count < anim.NumAnimCommands; count++)
                            {
                                var command = *pointer;
                                ++pointer;
                                switch ((TR_ANIMCOMMAND) command)
                                {
                                    case TR_ANIMCOMMAND.PlayEffect:
                                    case TR_ANIMCOMMAND.PlaySound:
                                        // Recalculate absolute frame number to relative.
                                        pointer[0] -= (short) trAnimation.FrameStart;
                                        pointer += 2;
                                        break;

                                    case TR_ANIMCOMMAND.SetPosition:
                                        // Parse through 3 operands.
                                        pointer += 3;
                                        break;

                                    case TR_ANIMCOMMAND.JumpDistance:
                                        // Parse through 2 operands.
                                        pointer += 2;
                                        break;

                                    default:
                                        // All other commands have no operands.
                                        break;
                                }
                            }
                        }
                    }
                }

                if (anim.Frames.Count == 0)
                {
                    // number of animations must be >= 1, because frame contains base model offset
                    anim.Frames.Resize(1, () => new BoneFrame());
                }

                // let us begin to load animations
                foreach (var boneFrame in anim.Frames)
                {
                    boneFrame.BoneTags.Resize(model.MeshCount, () => new BoneTag());
                    boneFrame.Position = Vector3.Zero;
                    boneFrame.Move = Vector3.Zero;
                    TR_GetBFrameBB_Pos(tr, (int) frameOffset, boneFrame);

                    if (frameOffset >= tr.FrameData.Length)
                    {
                        for (var k = 0; k < boneFrame.BoneTags.Count; k++)
                        {
                            treeTag = model.MeshTree[k];
                            var boneTag = boneFrame.BoneTags[k];
                            VMath.Vec4_SetTRRotations(ref boneTag.QRotate, Vector3.Zero);
                            boneTag.Offset = treeTag.Offset;
                        }
                    }
                    else
                    {
                        var l = l_start;
                        ushort temp1, temp2;
                        float ang;

                        for (var k = 0; k < boneFrame.BoneTags.Count; k++)
                        {
                            treeTag = model.MeshTree[k];
                            var boneTag = boneFrame.BoneTags[k];
                            VMath.Vec4_SetTRRotations(ref boneTag.QRotate, Vector3.Zero);
                            boneTag.Offset = treeTag.Offset;

                            switch (tr.GameVersion)
                            {
                                case TRGame.TR1:
                                case TRGame.TR1UnfinishedBusiness:
                                case TRGame.TR1Demo:
                                    temp2 = tr.FrameData[frameOffset + l];
                                    l++;
                                    temp1 = tr.FrameData[frameOffset + l];
                                    l++;
                                    VMath.Vec4_SetTRRotations(ref boneTag.QRotate, new Vector3(
                                        (temp1 & 0x3ff0) >> 4,
                                        -(((temp1 & 0x000f) << 6) | ((temp2 & 0xfc00) >> 10)),
                                        temp2 & 0x03ff) * (360.0f / 1024.0f));
                                    break;

                                default:
                                    temp1 = tr.FrameData[frameOffset + l];
                                    l++;
                                    if (tr.GameVersion >= TRGame.TR4)
                                    {
                                        ang = (temp1 & 0x0fff) * (360.0f / 4096.0f);
                                    }
                                    else
                                    {
                                        ang = (temp1 & 0x03ff) * (360.0f / 1024.0f);
                                    }

                                    switch (temp1 & 0xc000)
                                    {
                                        case 0x4000: // x only
                                            VMath.Vec4_SetTRRotations(ref boneTag.QRotate, new Vector3(ang, 0, 0));
                                            break;

                                        case 0x8000: // y only
                                            VMath.Vec4_SetTRRotations(ref boneTag.QRotate, new Vector3(0, 0, -ang));
                                            break;

                                        case 0xc000: // z only
                                            VMath.Vec4_SetTRRotations(ref boneTag.QRotate, new Vector3(0, ang, 0));
                                            break;

                                        default: // all three
                                            temp2 = tr.FrameData[frameOffset + l];
                                            VMath.Vec4_SetTRRotations(ref boneTag.QRotate, new Vector3(
                                                (temp1 & 0x3ff0) >> 4,
                                                -(((temp1 & 0x000f) << 6) | ((temp2 & 0xfc00) >> 10)),
                                                temp2 & 0x03ff) * (360.0f / 1024.0f));
                                            l++;
                                            break;
                                    }
                                    break;
                            }
                        }
                    }

                    frameOffset += frameStep;
                }
            }

            // Animations interpolation to 1/30 sec like in original. Needed for correct state change works.
            model.InterpolateFrames();
            // state change's loading

            if (LOG_ANIM_DISPATCHES)
            {
                if (model.Animations.Count > 1)
                {
                    Sys.DebugLog(LOG_FILENAME, "MODEL[{0}], anims = {1}", model_num, model.Animations.Count);
                }
            }
            for (var i = 0; i < model.Animations.Count; i++)
            {
                var anim = model.Animations[i];
                anim.StateChange.Clear();

                var trAnimation = tr.Animations[trMoveable.AnimationIndex + i];
                var animId = (trAnimation.NextAnimation - trMoveable.AnimationIndex) & 0x7fff;
                    // this masks out the sign bit
                Assert(animId >= 0);
                if (animId < model.Animations.Count)
                {
                    anim.NextAnim = model.Animations[animId];
                    anim.NextFrame = Math.Max(0,
                        (trAnimation.NextFrame - tr.Animations[trAnimation.NextAnimation].FrameStart) %
                        anim.NextAnim.Frames.Count);

                    if (LOG_ANIM_DISPATCHES)
                    {
                        Sys.DebugLog(LOG_FILENAME, "ANIM[{0}], next_anim = {1}, next_frame = {2}", i,
                            (int) anim.NextAnim.ID,
                            anim.NextFrame);
                    }
                }
                else
                {
                    anim.NextAnim = null;
                    anim.NextFrame = 0;
                }

                anim.StateChange.Clear(); // TODO: Needed?

                if (trAnimation.NumStateChanges > 0 && model.Animations.Count > 1)
                {
                    if (LOG_ANIM_DISPATCHES)
                    {
                        Sys.DebugLog(LOG_FILENAME, "ANIM[{0}], next_anim = {1}, next_frame = {2}", i,
                            anim.NextAnim == null ? -1 : (int) anim.NextAnim.ID, anim.NextFrame);
                    }
                    anim.StateChange.Resize(trAnimation.NumStateChanges, () => new StateChange());

                    for (var j = 0; j < trAnimation.NumStateChanges; j++)
                    {
                        var schP = anim.StateChange[j];
                        var trSch = tr.StateChanges[j + trAnimation.StateChangeOffset];
                        schP.ID = (TR_STATE) trSch.StateID;
                        schP.AnimDispatch.Clear();
                        for (var l = 0; l < trSch.NumAnimDispatches; l++)
                        {
                            var trAdisp = tr.AnimDispatches[trSch.AnimDispatch + l];
                            var nextAnim = trAdisp.NextAnimation & 0x7fff;
                            var nextAnimInd = nextAnim - (trMoveable.AnimationIndex & 0x7fff);
                            if (nextAnimInd < model.Animations.Count)
                            {
                                var adsp = new AnimDispatch();
                                var nextFramesCount =
                                    model.Animations[nextAnim - trMoveable.AnimationIndex].Frames.Count;
                                var nextFrame = trAdisp.NextFrame - tr.Animations[nextAnim].FrameStart;

                                var low = trAdisp.Low - trAnimation.FrameStart;
                                var high = trAdisp.High - trAnimation.FrameStart;

                                adsp.FrameLow = (ushort) (low % anim.Frames.Count);
                                adsp.FrameHigh = (ushort) ((high - 1) % anim.Frames.Count);
                                adsp.NextAnim = (TR_ANIMATION) (nextAnim - trMoveable.AnimationIndex);
                                adsp.NextFrame = (ushort) (nextFrame % nextFramesCount);

                                schP.AnimDispatch.Add(adsp);

                                if (LOG_ANIM_DISPATCHES)
                                {
                                    Sys.DebugLog(LOG_FILENAME,
                                        "anim_disp[{0}], frames.size() = {1}: interval[{2}.. {3}], next_anim = {4}, next_frame = {5}",
                                        l, anim.Frames.Count, adsp.FrameLow, adsp.FrameHigh, (int) adsp.NextAnim,
                                        adsp.NextFrame);
                                }
                            }
                        }
                    }
                }
            }
            GenerateAnimCommandsTransform(model);
        }
Exemplo n.º 3
0
        public static void TR_GenSkeletalModels(World world, Level tr)
        {
            world.SkeletalModels.Resize(tr.Moveables.Length);

            for (var i = 0; i < tr.Moveables.Length; i++)
            {
                var tr_moveable = tr.Moveables[i];
                var smodel = new SkeletalModel();
                smodel.ID = tr_moveable.ObjectID;
                smodel.MeshCount = tr_moveable.NumMeshes;
                TR_GenSkeletalModel(world, i, smodel, tr);
                smodel.FillTransparency();
                world.SkeletalModels[i] = smodel;
            }
        }
Exemplo n.º 4
0
        private void createHairMesh(SkeletalModel model)
        {
            Mesh = new BaseMesh();
            Mesh.ElementsPerTexture.Resize(EngineWorld.Textures.Count);
            var totalElements = 0;

            // Gather size information
            for (var i = 0; i < model.MeshCount; i++)
            {
                var original = model.MeshTree[i].MeshBase;

                Mesh.TexturePageCount = Math.Max(Mesh.TexturePageCount, original.TexturePageCount);

                for (var j = 0; j < original.TexturePageCount; j++)
                {
                    Mesh.ElementsPerTexture[j] += original.ElementsPerTexture[j];
                    totalElements += (int) original.ElementsPerTexture[j];
                }
            }

            // Create arrays
            Mesh.Elements.Resize(totalElements);

            // - with matrix index information
            Mesh.MatrixIndices.Resize(Mesh.Vertices.Count, () => new BaseMesh.MatrixIndex());

            // Copy information
            var elementsStartPerTexture = new List<uint>();
            elementsStartPerTexture.Resize((int)Mesh.TexturePageCount);
            Mesh.Vertices.Clear();
            for (var i = 0; i < model.MeshCount; i++)
            {
                var original = model.MeshTree[i].MeshBase;

                // Copy vertices
                var verticesStart = Mesh.Vertices.Count; // TODO: Wut... size == 0 (cf. L328)
                Mesh.Vertices.AddRange(original.Vertices);

                // Copy elements
                var originalElementsStart = 0;
                for (var page = 0; page < original.TexturePageCount; page++)
                {
                    if (original.ElementsPerTexture[page] == 0)
                        continue;

                    Assert(originalElementsStart < original.Elements.Count);
                    Assert(originalElementsStart + original.ElementsPerTexture[page] <= original.Elements.Count);

                    Assert(elementsStartPerTexture[page] < Mesh.Elements.Count);
                    Assert(elementsStartPerTexture[page] + original.ElementsPerTexture[page] <= Mesh.Elements.Count);

                    Helper.ListCopy(original.Elements, originalElementsStart, Mesh.Elements,
                        (int) elementsStartPerTexture[page], (int) original.ElementsPerTexture[page]);

                    for (var j = 0; j < original.ElementsPerTexture[page]; j++)
                    {
                        Mesh.Elements[(int) elementsStartPerTexture[page]] =
                            (uint) (verticesStart + original.Elements[originalElementsStart]);
                        originalElementsStart++;
                        elementsStartPerTexture[page]++;
                    }
                }

                // Apply total offset from parent.
                // The resulting mesh will have all the hair in default position
                // (i.e. as one big rope). The shader and matrix then transform it
                // correctly.
                Elements[i].Position = model.MeshTree[i].Offset;
                if(i > 0)
                {
                    // TODO: This assumes the parent is always the preceding mesh.
                    // True for hair, obviously wrong for everything else. Can stay
                    // here, but must go when we start generalizing the whole thing.
                    Elements[i].Position += Elements[i - 1].Position;
                }

                // And create vertex data (including matrix indices)
                for (var j = 0; j < original.Vertices.Count; j++)
                {
                    Mesh.MatrixIndices.Add(new BaseMesh.MatrixIndex());
                    Assert(Mesh.MatrixIndices.Count > verticesStart + j);
                    if (original.Vertices[j].Position[1] <= 0)
                    {
                        Mesh.MatrixIndices[verticesStart + j].I = (sbyte) i;
                        Mesh.MatrixIndices[verticesStart + j].J = (sbyte) (i + 1);
                    }
                    else
                    {
                        Mesh.MatrixIndices[verticesStart + j].I = (sbyte) (i + 1);
                        Mesh.MatrixIndices[verticesStart + j].J = Math.Min((sbyte) (i + 2), (sbyte) model.MeshCount);
                    }

                    // Now move all the hair vertices
                    Mesh.Vertices[verticesStart + j].Position += Elements[i].Position;

                    // If the normal isn't fully in y direction, cancel its y component
                    // This is perhaps a bit dubious.
                    if (Mesh.Vertices[verticesStart + j].Normal.X != 0 || Mesh.Vertices[verticesStart + j].Normal.Z != 0)
                    {
                        Mesh.Vertices[verticesStart + j].Normal.Y = 0;
                        Mesh.Vertices[verticesStart + j].Normal.Normalize();
                    }
                }
            }

            Mesh.GenVBO(Renderer);
        }
Exemplo n.º 5
0
        public void Prepare()
        {
            ID = 0;
            Name = "";
            Type = 0;
            Meshes = new List<BaseMesh>();
            Sprites = new List<Sprite>();
            Rooms = new List<Room>();
            FlipData = new List<FlipInfo>();
            Textures = new List<uint>();
            EntityTree = new Dictionary<uint, Entity>();
            ItemsTree = new Dictionary<uint, BaseItem>();
            Character = null;

#if !NO_AUDIO
            AudioSources = new List<AudioSource>();
            AudioBuffers = new uint[0];
            AudioEffects = new List<AudioEffect>();
            AudioEmitters = new List<AudioEmitter>();
            AudioMap = new List<short>();
            StreamTracks = new List<StreamTrack>();
            StreamTrackMap = new List<byte>();
#endif
            AnimSequences = new List<AnimSeq>();

            RoomBoxes = new List<RoomBox>();
            CamerasSinks = new List<StatCameraSink>();
            SkeletalModels = new List<SkeletalModel>();
            SkyBox = null;
            AnimCommands = new short[0];
        }
Exemplo n.º 6
0
        public void FromModel(SkeletalModel model)
        {
            HasSkin = false;
            BBMin = Vector3.Zero;
            BBMax = Vector3.Zero;
            Centre = Vector3.Zero;
            Position = Vector3.Zero;
            Animations = new SSAnimation();

            Animations.Model = model;
            BoneTags.Resize(model.MeshCount, () => new SSBoneTag());

            var stack = 0;
            var parents = new List<SSBoneTag>();
            parents.Resize(BoneTags.Count, () => new SSBoneTag());
            parents[0] = null;
            BoneTags[0].Parent = null;
            for (ushort i = 0; i < BoneTags.Count; i++)
            {
                BoneTags[i].Index = i;
                BoneTags[i].MeshBase = model.MeshTree[i].MeshBase;
                BoneTags[i].MeshSkin = model.MeshTree[i].MeshSkin;
                if (BoneTags[i].MeshSkin != null)
                    HasSkin = true;
                BoneTags[i].MeshSlot = null;
                BoneTags[i].BodyPart = model.MeshTree[i].BodyPart;

                BoneTags[i].Offset = model.MeshTree[i].Offset;
                BoneTags[i].QRotate = new Quaternion(0, 0, 0, 0);
                BoneTags[i].Transform.SetIdentity();
                BoneTags[i].FullTransform.SetIdentity();

                if (i > 0)
                {
                    BoneTags[i].Parent = BoneTags[i - 1];
                    if (model.MeshTree[i].Flag.HasFlagUns(0x01)) // POP
                    {
                        if (stack > 0)
                        {
                            BoneTags[i].Parent = parents[stack];
                            stack--;
                        }
                    }
                    if (model.MeshTree[i].Flag.HasFlagUns(0x02)) // PUSH
                    {
                        if (stack + 1 < (short) model.MeshCount)
                        {
                            stack++;
                            parents[stack] = BoneTags[i].Parent;
                        }
                    }
                }
            }
        }