public static void TR_GenRoom(int roomIndex, Room room, World world, Level tr) { var trRoom = tr.Rooms[roomIndex]; #region Room properties room.ID = (uint) roomIndex; room.Active = true; room.Frustum = new List<Frustum>(); room.Flags = trRoom.Flags; room.LightMode = trRoom.LightMode; room.ReverbInfo = (byte) trRoom.ReverbInfo; room.WaterScheme = trRoom.WaterScheme; room.AlternateGroup = (byte) trRoom.AlternateGroup; room.Transform = new Transform(); room.Transform.SetIdentity(); room.Transform.Origin = trRoom.Offset.ToVector3(); room.AmbientLighting = new float[3]; room.AmbientLighting[0] = trRoom.LightColor.R * 2; room.AmbientLighting[1] = trRoom.LightColor.G * 2; room.AmbientLighting[2] = trRoom.LightColor.B * 2; room.Self = new EngineContainer(); room.Self.Room = room; room.Self.Object = room; room.Self.ObjectType = OBJECT_TYPE.RoomBase; room.NearRoomList = new List<Room>(); room.OverlappedRoomList = new List<Room>(); room.GenMesh(world, (uint) roomIndex, tr); room.BtBody = null; // let's load static room meshes room.StaticMesh = new List<StaticMesh>(); #endregion #region Static meshes Loader.StaticMesh trStatic; for (var i = 0; i < trRoom.StaticMeshes.Length; i++) { var trsm = trRoom.StaticMeshes[i]; trStatic = tr.FindStaticMeshById(trsm.ObjectID); if (trStatic.Equals(default(Loader.StaticMesh))) { continue; } var rStatic = new StaticMesh(); rStatic.Self = new EngineContainer(); rStatic.Self.Room = room; rStatic.Self.Object = rStatic; rStatic.Self.ObjectType = OBJECT_TYPE.StaticMesh; rStatic.ObjectID = trsm.ObjectID; rStatic.Mesh = world.Meshes[(int) tr.MeshIndices[trStatic.Mesh]]; rStatic.Position = trsm.Position.ToVector3(); rStatic.Rotation = new Vector3(trsm.Rotation, 0.0f, 0.0f); rStatic.Tint[0] = trsm.Tint.R * 2; rStatic.Tint[1] = trsm.Tint.G * 2; rStatic.Tint[2] = trsm.Tint.B * 2; rStatic.Tint[3] = trsm.Tint.A * 2; rStatic.CBBMin.X = trStatic.CollisionBox[0].X; rStatic.CBBMin.Y = -trStatic.CollisionBox[0].Z; rStatic.CBBMin.Z = trStatic.CollisionBox[1].Y; rStatic.CBBMax.X = trStatic.CollisionBox[1].X; rStatic.CBBMax.Y = -trStatic.CollisionBox[1].Z; rStatic.CBBMax.Z = trStatic.CollisionBox[0].Y; rStatic.VBBMin.X = trStatic.VisibilityBox[0].X; rStatic.VBBMin.Y = -trStatic.VisibilityBox[0].Z; rStatic.VBBMin.Z = trStatic.VisibilityBox[1].Y; rStatic.VBBMax.X = trStatic.VisibilityBox[1].X; rStatic.VBBMax.Y = -trStatic.VisibilityBox[1].Z; rStatic.VBBMax.Z = trStatic.VisibilityBox[0].Y; rStatic.OBB.Transform = rStatic.Transform; rStatic.OBB.Radius = rStatic.Mesh.Radius; rStatic.Transform.SetIdentity(); VMath.Mat4_Translate(rStatic.Transform, rStatic.Position); VMath.Mat4_RotateZ(rStatic.Transform, rStatic.Rotation.X); rStatic.WasRendered = 0; rStatic.OBB.Rebuild(rStatic.VBBMin, rStatic.VBBMax); rStatic.OBB.DoTransform(); rStatic.BtBody = null; rStatic.Hide = false; // Disable static mesh collision, if flag value is 3 (TR1) or all bounding box // coordinates are equal (TR2-5). if (trStatic.Flags == 3 || trStatic.CollisionBox[0].X == -trStatic.CollisionBox[0].Y && trStatic.CollisionBox[0].Y == trStatic.CollisionBox[0].Z && trStatic.CollisionBox[1].X == -trStatic.CollisionBox[1].Y && trStatic.CollisionBox[1].Y == trStatic.CollisionBox[1].Z) { rStatic.Self.CollisionType = COLLISION_TYPE.None; } else { rStatic.Self.CollisionType = COLLISION_TYPE.Static; rStatic.Self.CollisionShape = COLLISION_SHAPE.Box; } // Set additional static mesh properties from level script override. Res_SetStaticMeshProperties(rStatic); // Set static mesh collision. if (rStatic.Self.CollisionType != COLLISION_TYPE.None) { CollisionShape cshape; switch (rStatic.Self.CollisionShape) { case COLLISION_SHAPE.Box: cshape = BT_CSfromBBox(rStatic.CBBMin, rStatic.CBBMax, true, true); break; case COLLISION_SHAPE.BoxBase: cshape = BT_CSfromBBox(rStatic.Mesh.BBMin, rStatic.Mesh.BBMax, true, true); break; case COLLISION_SHAPE.Trimesh: cshape = BT_CSfromMesh(rStatic.Mesh, true, true, true); break; case COLLISION_SHAPE.TrimeshConvex: cshape = BT_CSfromMesh(rStatic.Mesh, true, true, true); break; default: cshape = null; break; } if (cshape != null) { var startTransform = rStatic.Transform; var motionState = new DefaultMotionState(((Matrix4) startTransform).ToBullet()); var localInertia = Vector3.Zero; rStatic.BtBody = new RigidBody(new RigidBodyConstructionInfo(0.0f, motionState, cshape, localInertia.ToBullet())); BtEngineDynamicsWorld.AddRigidBody(rStatic.BtBody, CollisionFilterGroups.AllFilter, CollisionFilterGroups.AllFilter); rStatic.BtBody.UserObject = rStatic.Self; } } room.StaticMesh.Add(rStatic); } #endregion #region Sprites foreach (var trs in trRoom.Sprites) { var rs = new RoomSprite(); if (trs.Texture.IsBetween(0, world.Sprites.Count, IB.aIbE)) { rs.Sprite = world.Sprites[trs.Texture]; rs.Position = trRoom.Vertices[trs.Vertex].Vertex.ToVector3() + room.Transform.Origin; } room.Sprites.Add(rs); } #endregion #region Sectors room.SectorsX = trRoom.Num_X_Sectors; room.SectorsY = trRoom.Num_Z_Sectors; room.Sectors = new List<RoomSector>(); room.Sectors.Resize(room.SectorsX * room.SectorsY, () => new RoomSector()); // base sectors information loading and collisional mesh creation // To avoid manipulating with unnecessary information, we declare simple // heightmap here, which will be operated with sector and floordata parsing, // then vertical inbetween polys will be constructed, and Bullet collisional // object will be created. Afterwards, this heightmap also can be used to // quickly detect slopes for pushable blocks and other entities that rely on // floor level. for (var i = 0; i < room.Sectors.Count; i++) { var sector = room.Sectors[i]; // Filling base sectors information. sector.IndexX = (short) (i / room.SectorsY); sector.IndexY = (short) (i % room.SectorsY); sector.Position.X = room.Transform.Origin.X + (sector.IndexX + 0.5f) * TR_METERING_SECTORSIZE; sector.Position.Y = room.Transform.Origin.Y + (sector.IndexY + 0.5f) * TR_METERING_SECTORSIZE; sector.Position.Z = 0.5f * (trRoom.Y_Bottom + trRoom.Y_Top); sector.OwnerRoom = room; if (tr.GameVersion < TRGame.TR3) { sector.BoxIndex = trRoom.Sectors[i].Box_Index; sector.Material = SectorMaterial.Stone; } else { sector.BoxIndex = (trRoom.Sectors[i].Box_Index & 0xFFF0) >> 4; sector.Material = (SectorMaterial)((uint) trRoom.Sectors[i].Box_Index & 0x000F); } if (sector.BoxIndex == 0xFFFF) sector.BoxIndex = -1; sector.Flags = 0; // Clear sector flags sector.Floor = (int) -TR_METERING_STEP * trRoom.Sectors[i].Floor; sector.Ceiling = (int) -TR_METERING_STEP * trRoom.Sectors[i].Ceiling; sector.TrigIndex = trRoom.Sectors[i].FD_Index; // BUILDING CEILING HEIGHTMAP. // Penetration config is used later to build inbetween vertical collision polys. // If sector's penetration config is a wall, we simply build a vertical plane to // isolate this sector from top to bottom. Also, this allows to trick out wall // sectors inside another wall sectors to be ignored completely when building // collisional mesh. // Door penetration config means that we should either ignore sector collision // completely (classic door) or ignore one of the triangular sector parts (TR3+). if (sector.Ceiling == TR_METERING_WALLHEIGHT) { sector.CeilingPenetrationConfig = TR_PENETRATION_CONFIG.Wall; } else if (trRoom.Sectors[i].RoomAbove != 0xFF) { sector.CeilingPenetrationConfig = TR_PENETRATION_CONFIG.Ghost; } else { sector.CeilingPenetrationConfig = TR_PENETRATION_CONFIG.Solid; } // Reset some sector parameters to avoid garbaged memory issues. sector.PortalToRoom = -1; sector.CeilingDiagonalType = TR_SECTOR_DIAGONAL_TYPE.None; sector.FloorDiagonalType = TR_SECTOR_DIAGONAL_TYPE.None; // Now, we define heightmap cells position and draft (flat) height. // Draft height is derived from sector's floor and ceiling values, which are // copied into heightmap cells Y coordinates. As result, we receive flat // heightmap cell, which will be operated later with floordata. sector.CeilingCorners[0][0] = sector.IndexX * TR_METERING_SECTORSIZE; sector.CeilingCorners[0][1] = sector.IndexY * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.CeilingCorners[0][2] = sector.Ceiling; sector.CeilingCorners[1][0] = sector.IndexX * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.CeilingCorners[1][1] = sector.IndexY * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.CeilingCorners[1][2] = sector.Ceiling; sector.CeilingCorners[2][0] = sector.IndexX * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.CeilingCorners[2][1] = sector.IndexY * TR_METERING_SECTORSIZE; sector.CeilingCorners[2][2] = sector.Ceiling; sector.CeilingCorners[3][0] = sector.IndexX * TR_METERING_SECTORSIZE; sector.CeilingCorners[3][1] = sector.IndexY * TR_METERING_SECTORSIZE; sector.CeilingCorners[3][2] = sector.Ceiling; // BUILDING FLOOR HEIGHTMAP. // Features same steps as for the ceiling. if (sector.Floor == TR_METERING_WALLHEIGHT) { sector.FloorPenetrationConfig = TR_PENETRATION_CONFIG.Wall; } else if (trRoom.Sectors[i].RoomBelow != 0xFF) { sector.FloorPenetrationConfig = TR_PENETRATION_CONFIG.Ghost; } else { sector.FloorPenetrationConfig = TR_PENETRATION_CONFIG.Solid; } sector.FloorCorners[0][0] = sector.IndexX * TR_METERING_SECTORSIZE; sector.FloorCorners[0][1] = sector.IndexY * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.FloorCorners[0][2] = sector.Floor; sector.FloorCorners[1][0] = sector.IndexX * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.FloorCorners[1][1] = sector.IndexY * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.FloorCorners[1][2] = sector.Floor; sector.FloorCorners[2][0] = sector.IndexX * TR_METERING_SECTORSIZE + TR_METERING_SECTORSIZE; sector.FloorCorners[2][1] = sector.IndexY * TR_METERING_SECTORSIZE; sector.FloorCorners[2][2] = sector.Floor; sector.FloorCorners[3][0] = sector.IndexX * TR_METERING_SECTORSIZE; sector.FloorCorners[3][1] = sector.IndexY * TR_METERING_SECTORSIZE; sector.FloorCorners[3][2] = sector.Floor; } #endregion #region Lights room.Lights.Resize(trRoom.Lights.Length, () => new Light()); for (var i = 0; i < trRoom.Lights.Length; i++) { var l = room.Lights[i]; var tl = trRoom.Lights[i]; l.LightType = tl.LightType; l.Position = tl.Position.ToVector3(); if (l.LightType == LightType.Shadow) { l.Colour[0] = -(tl.Color.R / 255.0f) * tl.Intensity; l.Colour[1] = -(tl.Color.G / 255.0f) * tl.Intensity; l.Colour[2] = -(tl.Color.B / 255.0f) * tl.Intensity; l.Colour[3] = 1.0f; } else { l.Colour[0] = tl.Color.R / 255.0f * tl.Intensity; l.Colour[1] = tl.Color.G / 255.0f * tl.Intensity; l.Colour[2] = tl.Color.B / 255.0f * tl.Intensity; l.Colour[3] = 1.0f; } l.Inner = tl.R_Inner; l.Outer = tl.R_Outer; l.Length = tl.Length; l.Cutoff = tl.Cutoff; l.Falloff = 0.001f / l.Outer; } #endregion #region Portals room.Portals.Resize(trRoom.Portals.Length, () => new Portal()); for (var i = 0; i < room.Portals.Count; i++) { var trp = trRoom.Portals[i]; var p = room.Portals[i]; var rDest = world.Rooms[trp.AdjoiningRoom]; p.Vertices.Resize(4); // in original TR all portals are axis aligned rectangles p.DestRoom = rDest; p.CurrentRoom = room; p.Vertices = trp.Vertices.Reverse().Select(x => x.ToVector3() + room.Transform.Origin).ToList(); p.Centre = p.Vertices.Sum() / p.Vertices.Count; p.GenNormal(); // Portal position fix // X_MIN if(p.Normal.Normal.X > 0.999f && (int)p.Centre.X % 2 != 0) { p.Move(Vector3.UnitX); } // Y_MIN if (p.Normal.Normal.Y > 0.999f && (int)p.Centre.Y % 2 != 0) { p.Move(Vector3.UnitY); } // Z_MAX if (p.Normal.Normal.Z < -0.999f && (int)p.Centre.Z % 2 != 0) { p.Move(-Vector3.UnitZ); } } #endregion #region Room borders room.BBMin.Z = trRoom.Y_Bottom; room.BBMax.Z = trRoom.Y_Top; room.BBMin.X = room.Transform.Origin.X + TR_METERING_SECTORSIZE; room.BBMin.Y = room.Transform.Origin.Y + TR_METERING_SECTORSIZE; room.BBMax.X = room.Transform.Origin.X + TR_METERING_SECTORSIZE * room.SectorsX - TR_METERING_SECTORSIZE; room.BBMax.Y = room.Transform.Origin.Y + TR_METERING_SECTORSIZE * room.SectorsY - TR_METERING_SECTORSIZE; #endregion #region Alternate room // alternate room pointer calculation if one exists. room.AlternateRoom = null; room.BaseRoom = null; if(trRoom.AlternateRoom.IsBetween(0, tr.Rooms.Length, IB.aIbE)) { room.AlternateRoom = world.Rooms[trRoom.AlternateRoom]; } #endregion }
public static void TR_GenEntities(World world, Level tr) { for (var i = 0; i < tr.Items.Length; i++) { var trItem = tr.Items[i]; var entity = trItem.ObjectID == 0 ? new Character((uint) i) : new Entity((uint) i); entity.Transform.Origin.X = trItem.Position.X; entity.Transform.Origin.Y = -trItem.Position.Z; entity.Transform.Origin.Z = trItem.Position.Y; entity.Angles.X = trItem.Rotation; entity.Angles.Y = 0; entity.Angles.Z = 0; entity.UpdateTransform(); entity.Self.Room = trItem.Room.IsBetween(0, world.Rooms.Count - 1) ? world.Rooms[trItem.Room] : null; entity.TriggerLayout = (ENTITY_TLAYOUT)trItem.ActivationMash; // FIXME: Ignore INVISIBLE and CLEAR BODY flags for a moment. entity.OCB = trItem.ObjectCodeBit; entity.Timer = 0.0f; entity.Self.CollisionType = COLLISION_TYPE.Kinematic; entity.Self.CollisionShape = COLLISION_SHAPE.TrimeshConvex; entity.MoveType = MoveType.StaticPos; entity.InertiaLinear = 0.0f; entity.InertiaAngular = Vector2.Zero; entity.Bf.Animations.Model = world.GetModelByID((uint)trItem.ObjectID); if(entity.Bf.Animations.Model == null) { var id = EngineLua.Call("getOverridedID", Loader.Helper.GameToEngine(tr.GameVersion), trItem.ObjectID)[0]; entity.Bf.Animations.Model = world.GetModelByID((uint) id); } var replaceAnimId = (int)EngineLua.Call("getOverridedAnim", Loader.Helper.GameToEngine(tr.GameVersion), trItem.ObjectID)[0]; if(replaceAnimId > 0) { var replaceAnimModel = world.GetModelByID((uint)replaceAnimId); var tmp = entity.Bf.Animations.Model.Animations; entity.Bf.Animations.Model.Animations = replaceAnimModel.Animations; replaceAnimModel.Animations = tmp; } if(entity.Bf.Animations.Model == null) { // SPRITE LOADING var sp = world.GetSpriteByID((uint)trItem.ObjectID); if(sp != null && entity.Self.Room != null) { var rsp = new RoomSprite(); rsp.Sprite = sp; rsp.Position = entity.Transform.Origin; rsp.WasRendered = false; entity.Self.Room.Sprites.Add(rsp); } continue; // that entity has no model. may be it is a some trigger or look at object } if(tr.GameVersion < TRGame.TR2 && trItem.ObjectID == 83) // FIXME: brutal magick hardcode! ;-) { // skip PSX save model continue; } entity.Bf.FromModel(entity.Bf.Animations.Model); if(trItem.ObjectID == 0) // Lara is unical model { var lara = (Character) entity; Assert(lara != null); lara.MoveType = MoveType.OnFloor; world.Character = lara; lara.Self.CollisionType = COLLISION_TYPE.Actor; lara.Self.CollisionShape = COLLISION_SHAPE.TrimeshConvex; lara.TypeFlags |= ENTITY_TYPE.TriggerActivator; SkeletalModel LM; EngineLua.Set("player", lara.ID); switch (Loader.Helper.GameToEngine(tr.GameVersion)) { case Loader.Engine.TR1: if (GameflowManager.LevelID == 0) { LM = world.GetModelByID((uint) TR_ITEM_LARA.AlternateTR1); if (LM != null) { // In TR1, Lara has unified head mesh for all her alternate skins. // Hence, we copy all meshes except head, to prevent Potato Raider bug. SkeletonCopyMeshes(world.SkeletalModels[0].MeshTree, LM.MeshTree, world.SkeletalModels[0].MeshCount - 1); } } break; case Loader.Engine.TR3: LM = world.GetModelByID((uint) TR_ITEM_LARA.TR3); if (LM != null) { SkeletonCopyMeshes(world.SkeletalModels[0].MeshTree, LM.MeshTree, world.SkeletalModels[0].MeshCount); var tmp = world.GetModelByID(11); // moto / quadro cycle animations if (tmp != null) { SkeletonCopyMeshes(tmp.MeshTree, LM.MeshTree, world.SkeletalModels[0].MeshCount); } } break; case Loader.Engine.TR4: case Loader.Engine.TR5: LM = world.GetModelByID((uint)TR_ITEM_LARA.TR4_5); // base skeleton meshes if (LM != null) { SkeletonCopyMeshes(world.SkeletalModels[0].MeshTree, LM.MeshTree, world.SkeletalModels[0].MeshCount); } LM = world.GetModelByID((uint)TR_ITEM_LARA.Joints_TR4_5); // skin skeleton meshes if (LM != null) { SkeletonCopyMeshes2(world.SkeletalModels[0].MeshTree, LM.MeshTree, world.SkeletalModels[0].MeshCount); } world.SkeletalModels[0].FillSkinnedMeshMap(); break; case Loader.Engine.Unknown: break; } for (var j = 0; j < lara.Bf.BoneTags.Count; j++) { lara.Bf.BoneTags[j].MeshBase = lara.Bf.Animations.Model.MeshTree[j].MeshBase; lara.Bf.BoneTags[j].MeshSkin = lara.Bf.Animations.Model.MeshTree[j].MeshSkin; lara.Bf.BoneTags[i].MeshSlot = null; } world.Character.SetAnimation(TR_ANIMATION.LaraStayIdle, 0); lara.GenRigidBody(); lara.CreateGhosts(); lara.Height = 768.0f; lara.StateFunc = AnimStateControl.StateControlLara; continue; } entity.SetAnimation(TR_ANIMATION.LaraRun, 0); // Set zero animation and zero frame Res_SetEntityProperties(entity); entity.RebuildBV(); entity.GenRigidBody(); entity.Self.Room.AddEntity(entity); world.AddEntity(entity); } }