Пример #1
0
        /// <summary>
        /// Open the specified WFile and load all the scenery objects into the viewer.
        /// If the file doesn't exist, then return an empty WorldFile object.
        /// </summary>
        /// <param name="visible">Tiles adjacent to the current visible tile may not be modelled.
        /// This flag decides whether a missing file leads to a warning message.</param>
        public WorldFile(Viewer viewer, int tileX, int tileZ, bool visible)
        {
            Viewer = viewer;
            TileX  = tileX;
            TileZ  = tileZ;

            var cancellation = Viewer.LoaderProcess.CancellationToken;

            // determine file path to the WFile at the specified tile coordinates
            var WFileName = WorldFileNameFromTileCoordinates(tileX, tileZ);
            var WFilePath = viewer.Simulator.RoutePath + @"\World\" + WFileName;

            // if there isn't a file, then return with an empty WorldFile object
            if (!File.Exists(WFilePath))
            {
                if (visible)
                {
                    Trace.TraceWarning("World file missing - {0}", WFilePath);
                }
                return;
            }

            // read the world file
            var WFile = new Orts.Formats.Msts.WorldFile(WFilePath);

            // check for existence of world file in OpenRails subfolder

            WFilePath = viewer.Simulator.RoutePath + @"\World\Openrails\" + WFileName;
            if (File.Exists(WFilePath))
            {
                // We have an OR-specific addition to world file
                WFile.InsertORSpecificData(WFilePath);
            }



            // to avoid loop checking for every object this pre-check is performed
            bool containsMovingTable = false;

            if (Program.Simulator.MovingTables != null)
            {
                foreach (var movingTable in Program.Simulator.MovingTables)
                {
                    if (movingTable.WFile == WFileName)
                    {
                        containsMovingTable = true;
                        break;
                    }
                }
            }

            // create all the individual scenery objects specified in the WFile
            foreach (var worldObject in WFile.Tr_Worldfile)
            {
                if (worldObject.StaticDetailLevel > viewer.Settings.WorldObjectDensity)
                {
                    continue;
                }

                // If the loader has been asked to temrinate, bail out early.
                if (cancellation.IsCancellationRequested)
                {
                    break;
                }

                // Get the position of the scenery object into ORTS coordinate space.
                WorldPosition worldMatrix;
                if (worldObject.Matrix3x3 != null && worldObject.Position != null)
                {
                    worldMatrix = WorldPositionFromMSTSLocation(WFile.TileX, WFile.TileZ, worldObject.Position, worldObject.Matrix3x3);
                }
                else if (worldObject.QDirection != null && worldObject.Position != null)
                {
                    worldMatrix = WorldPositionFromMSTSLocation(WFile.TileX, WFile.TileZ, worldObject.Position, worldObject.QDirection);
                }
                else
                {
                    Trace.TraceWarning("{0} scenery object {1} is missing Matrix3x3 and QDirection", WFileName, worldObject.UID);
                    continue;
                }

                var shadowCaster  = (worldObject.StaticFlags & (uint)StaticFlag.AnyShadow) != 0 || viewer.Settings.ShadowAllShapes;
                var animated      = (worldObject.StaticFlags & (uint)StaticFlag.Animate) != 0;
                var isAnalogClock = GetClockType(worldObject.FileName) == ClockType.Analog;
                var global        = (worldObject is TrackObj) || (worldObject is HazardObj) || (worldObject.StaticFlags & (uint)StaticFlag.Global) != 0;

                // TransferObj have a FileName but it is not a shape, so we need to avoid sanity-checking it as if it was.
                var fileNameIsNotShape = (worldObject is TransferObj || worldObject is HazardObj);

                // Determine the file path to the shape file for this scenery object and check it exists as expected.
                var shapeFilePath = fileNameIsNotShape || String.IsNullOrEmpty(worldObject.FileName) ? null : global ? viewer.Simulator.BasePath + @"\Global\Shapes\" + worldObject.FileName : viewer.Simulator.RoutePath + @"\Shapes\" + worldObject.FileName;
                if (shapeFilePath != null)
                {
                    shapeFilePath = Path.GetFullPath(shapeFilePath);
                    if (!File.Exists(shapeFilePath))
                    {
                        Trace.TraceWarning("{0} scenery object {1} with StaticFlags {3:X8} references non-existent {2}", WFileName, worldObject.UID, shapeFilePath, worldObject.StaticFlags);
                        shapeFilePath = null;
                    }
                }

                if (shapeFilePath != null && File.Exists(shapeFilePath + "d"))
                {
                    var shape = new ShapeDescriptorFile(shapeFilePath + "d");
                    if (shape.shape.ESD_Bounding_Box != null)
                    {
                        var min       = shape.shape.ESD_Bounding_Box.Min;
                        var max       = shape.shape.ESD_Bounding_Box.Max;
                        var transform = Matrix.Invert(worldMatrix.XNAMatrix);
                        // Not sure if this is needed, but it is to correct for center-of-gravity being not the center of the box.
                        //transform.M41 += (max.X + min.X) / 2;
                        //transform.M42 += (max.Y + min.Y) / 2;
                        //transform.M43 += (max.Z + min.Z) / 2;
                        BoundingBoxes.Add(new BoundingBox(transform, new Vector3((max.X - min.X) / 2, (max.Y - min.Y) / 2, (max.Z - min.Z) / 2), worldMatrix.XNAMatrix.Translation.Y));
                    }
                }

                try
                {
                    if (worldObject.GetType() == typeof(TrackObj))
                    {
                        var trackObj = (TrackObj)worldObject;
                        // Switch tracks need a link to the simulator engine so they can animate the points.
                        var trJunctionNode = trackObj.JNodePosn != null?viewer.Simulator.TDB.GetTrJunctionNode(TileX, TileZ, (int)trackObj.UID) : null;

                        // We might not have found the junction node; if so, fall back to the static track shape.
                        if (trJunctionNode != null)
                        {
                            if (viewer.Simulator.UseSuperElevation > 0)
                            {
                                SuperElevationManager.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath);
                            }
                            sceneryObjects.Add(new SwitchTrackShape(viewer, shapeFilePath, worldMatrix, trJunctionNode));
                        }
                        else
                        {
                            //if want to use super elevation, we will generate tracks using dynamic tracks
                            if (viewer.Simulator.UseSuperElevation > 0 &&
                                SuperElevationManager.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath))
                            {
                                //var success = SuperElevation.DecomposeStaticSuperElevation(viewer, dTrackList, trackObj, worldMatrix, TileX, TileZ, shapeFilePath);
                                //if (success == 0) sceneryObjects.Add(new StaticTrackShape(viewer, shapeFilePath, worldMatrix));
                            }
                            //otherwise, use shapes
                            else if (!containsMovingTable)
                            {
                                sceneryObjects.Add(new StaticTrackShape(viewer, shapeFilePath, worldMatrix));
                            }
                            else
                            {
                                var found = false;
                                foreach (var movingTable in Program.Simulator.MovingTables)
                                {
                                    if (worldObject.UID == movingTable.UID && WFileName == movingTable.WFile)
                                    {
                                        found = true;
                                        if (movingTable is Simulation.Turntable)
                                        {
                                            var turntable = movingTable as Simulation.Turntable;
                                            turntable.ComputeCenter(worldMatrix);
                                            var startingY = Math.Asin(-2 * (worldObject.QDirection.A * worldObject.QDirection.C - worldObject.QDirection.B * worldObject.QDirection.D));
                                            sceneryObjects.Add(new TurntableShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None, turntable, startingY));
                                        }
                                        else
                                        {
                                            var transfertable = movingTable as Simulation.Transfertable;
                                            transfertable.ComputeCenter(worldMatrix);
                                            sceneryObjects.Add(new TransfertableShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None, transfertable));
                                        }
                                        break;
                                    }
                                }
                                if (!found)
                                {
                                    sceneryObjects.Add(new StaticTrackShape(viewer, shapeFilePath, worldMatrix));
                                }
                            }
                        }
                        if (viewer.Simulator.Settings.Wire == true && viewer.Simulator.TRK.Tr_RouteFile.Electrified == true &&
                            worldObject.StaticDetailLevel != 2 &&   // Make it compatible with routes that use 'HideWire', a workaround for MSTS that
                            worldObject.StaticDetailLevel != 3      // allowed a mix of electrified and non electrified track see http://msts.steam4me.net/tutorials/hidewire.html
                            )
                        {
                            int success = Wire.DecomposeStaticWire(viewer, dTrackList, trackObj, worldMatrix);
                            //if cannot draw wire, try to see if it is converted. modified for DynaTrax
                            if (success == 0 && trackObj.FileName.Contains("Dyna"))
                            {
                                Wire.DecomposeConvertedDynamicWire(viewer, dTrackList, trackObj, worldMatrix);
                            }
                        }
                    }
                    else if (worldObject.GetType() == typeof(DyntrackObj))
                    {
                        if (viewer.Simulator.Settings.Wire == true && viewer.Simulator.TRK.Tr_RouteFile.Electrified == true)
                        {
                            Wire.DecomposeDynamicWire(viewer, dTrackList, (DyntrackObj)worldObject, worldMatrix);
                        }
                        // Add DyntrackDrawers for individual subsections
                        if (viewer.Simulator.UseSuperElevation > 0 && SuperElevationManager.UseSuperElevationDyn(viewer, dTrackList, (DyntrackObj)worldObject, worldMatrix))
                        {
                            SuperElevationManager.DecomposeDynamicSuperElevation(viewer, dTrackList, (DyntrackObj)worldObject, worldMatrix);
                        }
                        else
                        {
                            DynamicTrack.Decompose(viewer, dTrackList, (DyntrackObj)worldObject, worldMatrix);
                        }
                    } // end else if DyntrackObj
                    else if (worldObject.GetType() == typeof(ForestObj))
                    {
                        if (!(worldObject as ForestObj).IsYard)
                        {
                            forestList.Add(new ForestViewer(viewer, (ForestObj)worldObject, worldMatrix));
                        }
                    }
                    else if (worldObject.GetType() == typeof(SignalObj))
                    {
                        sceneryObjects.Add(new SignalShape(viewer, (SignalObj)worldObject, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None));
                    }
                    else if (worldObject.GetType() == typeof(TransferObj))
                    {
                        sceneryObjects.Add(new TransferShape(viewer, (TransferObj)worldObject, worldMatrix));
                    }
                    else if (worldObject.GetType() == typeof(LevelCrossingObj))
                    {
                        sceneryObjects.Add(new LevelCrossingShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None, (LevelCrossingObj)worldObject));
                    }
                    else if (worldObject.GetType() == typeof(HazardObj))
                    {
                        var h = HazzardShape.CreateHazzard(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None, (HazardObj)worldObject);
                        if (h != null)
                        {
                            sceneryObjects.Add(h);
                        }
                    }
                    else if (worldObject.GetType() == typeof(SpeedPostObj))
                    {
                        sceneryObjects.Add(new SpeedPostShape(viewer, shapeFilePath, worldMatrix, (SpeedPostObj)worldObject));
                    }
                    else if (worldObject.GetType() == typeof(CarSpawnerObj))
                    {
                        if (Program.Simulator.CarSpawnerLists != null && ((CarSpawnerObj)worldObject).ListName != null)
                        {
                            ((CarSpawnerObj)worldObject).CarSpawnerListIdx = Program.Simulator.CarSpawnerLists.FindIndex(x => x.ListName == ((CarSpawnerObj)worldObject).ListName);
                            if (((CarSpawnerObj)worldObject).CarSpawnerListIdx < 0 || ((CarSpawnerObj)worldObject).CarSpawnerListIdx > Program.Simulator.CarSpawnerLists.Count - 1)
                            {
                                ((CarSpawnerObj)worldObject).CarSpawnerListIdx = 0;
                            }
                        }
                        else
                        {
                            ((CarSpawnerObj)worldObject).CarSpawnerListIdx = 0;
                        }
                        carSpawners.Add(new RoadCarSpawner(viewer, worldMatrix, (CarSpawnerObj)worldObject));
                    }
                    else if (worldObject.GetType() == typeof(SidingObj))
                    {
                        sidings.Add(new TrItemLabel(viewer, worldMatrix, (SidingObj)worldObject));
                    }
                    else if (worldObject.GetType() == typeof(PlatformObj))
                    {
                        platforms.Add(new TrItemLabel(viewer, worldMatrix, (PlatformObj)worldObject));
                    }
                    else if (worldObject.GetType() == typeof(StaticObj))
                    {
                        if (isAnalogClock)
                        {
                            sceneryObjects.Add(new AnalogClockShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None));
                        }
                        else if (animated)
                        {
                            sceneryObjects.Add(new AnimatedShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None));
                        }
                        else
                        {
                            sceneryObjects.Add(new StaticShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None));
                        }
                    }
                    else if (worldObject.GetType() == typeof(PickupObj))
                    {
                        sceneryObjects.Add(new FuelPickupItemShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None, (PickupObj)worldObject));
                        PickupList.Add((PickupObj)worldObject);
                    }
                    else // It's some other type of object - not one of the above.
                    {
                        sceneryObjects.Add(new StaticShape(viewer, shapeFilePath, worldMatrix, shadowCaster ? ShapeFlags.ShadowCaster : ShapeFlags.None));
                    }
                }
                catch (Exception error)
                {
                    Trace.WriteLine(new FileLoadException(String.Format("{0} scenery object {1} failed to load", worldMatrix, worldObject.UID), error));
                }
            }

            // Check if there are activity restricted speedposts to be loaded

            if (Viewer.Simulator.ActivityRun != null && Viewer.Simulator.Activity.Tr_Activity.Tr_Activity_File.ActivityRestrictedSpeedZones != null)
            {
                foreach (TempSpeedPostItem tempSpeedItem in Viewer.Simulator.ActivityRun.TempSpeedPostItems)
                {
                    if (tempSpeedItem.WorldPosition.TileX == TileX && tempSpeedItem.WorldPosition.TileZ == TileZ)
                    {
                        if (Viewer.SpeedpostDatFile == null)
                        {
                            Trace.TraceWarning(String.Format("{0} missing; speed posts for temporary speed restrictions in tile {1} {2} will not be visible.", Viewer.Simulator.RoutePath + @"\speedpost.dat", TileX, TileZ));
                            break;
                        }
                        else
                        {
                            sceneryObjects.Add(new StaticShape(viewer,
                                                               tempSpeedItem.IsWarning ? Viewer.SpeedpostDatFile.TempSpeedShapeNames[0] : (tempSpeedItem.IsResume ? Viewer.SpeedpostDatFile.TempSpeedShapeNames[2] : Viewer.SpeedpostDatFile.TempSpeedShapeNames[1]),
                                                               tempSpeedItem.WorldPosition, ShapeFlags.None));
                        }
                    }
                }
            }

            // Model instancing requires feature level 9_3 or higher.
            if (Viewer.Settings.ModelInstancing && Viewer.Settings.IsDirectXFeatureLevelIncluded(ORTS.Settings.UserSettings.DirectXFeature.Level9_3))
            {
                // Instancing collapsed multiple copies of the same model in to a single set of data (the normal model
                // data, plus a list of position information for each copy) and then draws them in a single batch.
                var instances = new Dictionary <string, List <StaticShape> >();
                foreach (var shape in sceneryObjects)
                {
                    // Only allow StaticShape and StaticTrackShape instances for now.
                    if (shape.GetType() != typeof(StaticShape) && shape.GetType() != typeof(StaticTrackShape))
                    {
                        continue;
                    }

                    // Must have a file path so we can collapse instances on something.
                    var path = shape.SharedShape.FilePath;
                    if (path == null)
                    {
                        continue;
                    }

                    if (path != null && !instances.ContainsKey(path))
                    {
                        instances.Add(path, new List <StaticShape>());
                    }

                    if (path != null)
                    {
                        instances[path].Add(shape);
                    }
                }
                foreach (var path in instances.Keys)
                {
                    if (instances[path].Count >= MinimumInstanceCount)
                    {
                        var sharedInstance = new SharedStaticShapeInstance(Viewer, path, instances[path]);
                        foreach (var model in instances[path])
                        {
                            sceneryObjects.Remove(model);
                        }
                        sceneryObjects.Add(sharedInstance);
                    }
                }
            }

            if (viewer.Simulator.UseSuperElevation > 0)
            {
                SuperElevationManager.DecomposeStaticSuperElevation(Viewer, dTrackList, TileX, TileZ);
            }
            if (Viewer.World.Sounds != null)
            {
                Viewer.World.Sounds.AddByTile(TileX, TileZ);
            }
        }
Пример #2
0
        /// <summary>
        /// Only one copy of the model is loaded regardless of how many copies are placed in the scene.
        /// </summary>
        void LoadContent()
        {
            Trace.Write("S");
            var filePath = FilePath;
            // commented lines allow reading the animation block from an additional file in an Openrails subfolder
            //           string dir = Path.GetDirectoryName(filePath);
            //            string file = Path.GetFileName(filePath);
            //            string orFilePath = dir + @"\openrails\" + file;
            var sFile = new ShapeFile(filePath, viewer.Settings.SuppressShapeWarnings);
            //            if (file.ToLower().Contains("turntable") && File.Exists(orFilePath))
            //            {
            //                sFile.ReadAnimationBlock(orFilePath);
            //            }


            var textureFlags = Helpers.TextureFlags.None;

            if (File.Exists(FilePath + "d"))
            {
                var sdFile = new ShapeDescriptorFile(FilePath + "d");
                textureFlags = (Helpers.TextureFlags)sdFile.Shape.EsdAlternativeTexture;
                if (FilePath != null && FilePath.Contains("\\global\\"))
                {
                    textureFlags |= Helpers.TextureFlags.SnowTrack;                                                     //roads and tracks are in global, as MSTS will always use snow texture in snow weather
                }
                HasNightSubObj = sdFile.Shape.EsdSubObject;
                if ((textureFlags & Helpers.TextureFlags.Night) != 0 && FilePath.Contains("\\trainset\\"))
                {
                    textureFlags |= Helpers.TextureFlags.Underground;
                }
                SoundFileName    = sdFile.Shape.EsdSoundFileName;
                BellAnimationFPS = sdFile.Shape.EsdBellAnimationFps;
            }

            Matrices    = sFile.Shape.Matrices.ToArray();
            MatrixNames = sFile.Shape.Matrices.MatrixNames;
            //var matrixCount = sFile.shape.matrices.Count;
            //MatrixNames.Capacity = matrixCount;
            //Matrices = new Matrix[matrixCount];
            //for (var i = 0; i < matrixCount; ++i)
            //{
            //    MatrixNames.Add(sFile.shape.matrices[i].Name.ToUpper());
            //    Matrices[i] = XNAMatrixFromMSTS(sFile.shape.matrices[i]);
            //}
            Animations = sFile.Shape.Animations;

#if DEBUG_SHAPE_HIERARCHY
            var debugShapeHierarchy = new StringBuilder();
            debugShapeHierarchy.AppendFormat("Shape {0}:\n", Path.GetFileNameWithoutExtension(FilePath).ToUpper());
            for (var i = 0; i < MatrixNames.Count; ++i)
            {
                debugShapeHierarchy.AppendFormat("  Matrix {0,-2}: {1}\n", i, MatrixNames[i]);
            }
            for (var i = 0; i < sFile.shape.prim_states.Count; ++i)
            {
                debugShapeHierarchy.AppendFormat("  PState {0,-2}: flags={1,-8:X8} shader={2,-15} alpha={3,-2} vstate={4,-2} lstate={5,-2} zbias={6,-5:F3} zbuffer={7,-2} name={8}\n", i, sFile.shape.prim_states[i].flags, sFile.shape.shader_names[sFile.shape.prim_states[i].ishader], sFile.shape.prim_states[i].alphatestmode, sFile.shape.prim_states[i].ivtx_state, sFile.shape.prim_states[i].LightCfgIdx, sFile.shape.prim_states[i].ZBias, sFile.shape.prim_states[i].ZBufMode, sFile.shape.prim_states[i].Name);
            }
            for (var i = 0; i < sFile.shape.vtx_states.Count; ++i)
            {
                debugShapeHierarchy.AppendFormat("  VState {0,-2}: flags={1,-8:X8} lflags={2,-8:X8} lstate={3,-2} material={4,-3} matrix2={5,-2}\n", i, sFile.shape.vtx_states[i].flags, sFile.shape.vtx_states[i].LightFlags, sFile.shape.vtx_states[i].LightCfgIdx, sFile.shape.vtx_states[i].LightMatIdx, sFile.shape.vtx_states[i].Matrix2);
            }
            for (var i = 0; i < sFile.shape.light_model_cfgs.Count; ++i)
            {
                debugShapeHierarchy.AppendFormat("  LState {0,-2}: flags={1,-8:X8} uv_ops={2,-2}\n", i, sFile.shape.light_model_cfgs[i].flags, sFile.shape.light_model_cfgs[i].uv_ops.Count);
                for (var j = 0; j < sFile.shape.light_model_cfgs[i].uv_ops.Count; ++j)
                {
                    debugShapeHierarchy.AppendFormat("    UV OP {0,-2}: texture_address_mode={1,-2}\n", j, sFile.shape.light_model_cfgs[i].uv_ops[j].TexAddrMode);
                }
            }
            Console.Write(debugShapeHierarchy.ToString());
#endif
            LodControls = (from Formats.Msts.Models.LodControl lod in sFile.Shape.LodControls
                           select new LodControl(lod, textureFlags, sFile, this)).ToArray();
            if (LodControls.Length == 0)
            {
                throw new InvalidDataException("Shape file missing lod_control section");
            }
            else if (LodControls[0].DistanceLevels.Length > 0 && LodControls[0].DistanceLevels[0].SubObjects.Length > 0)
            {
                // Zero the position offset of the root matrix for compatibility with MSTS
                if (LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives.Length > 0 && LodControls[0].DistanceLevels[0].SubObjects[0].ShapePrimitives[0].Hierarchy[0] == -1)
                {
                    Matrices[0].M41 = 0;
                    Matrices[0].M42 = 0;
                    Matrices[0].M43 = 0;
                }
                // Look for root subobject, it is not necessarily the first (see ProTrain signal)
                for (int soIndex = 0; soIndex <= LodControls[0].DistanceLevels[0].SubObjects.Length - 1; soIndex++)
                {
                    Formats.Msts.Models.SubObject subObject = sFile.Shape.LodControls[0].DistanceLevels[0].SubObjects[soIndex];
                    if (subObject.SubObjectHeader.GeometryInfo.GeometryNodeMap[0] == 0)
                    {
                        RootSubObjectIndex = soIndex;
                        break;
                    }
                }
            }
        }