/// <summary> /// Encodes material options code from parameterized options. /// Material options encoding is documented in SharedShape.SubObject() (Shapes.cs) /// or SceneryMaterial.SetState() (Materials.cs). /// </summary> /// <param name="lod">LODItem instance.</param> /// <returns>Options code.</returns> public static SceneryMaterialOptions EncodeMaterialOptions(LODItem lod) { var options = SceneryMaterialOptions.None; if (TextureAddressingModeNames.ContainsKey(lod.TexAddrModeName)) { options |= TextureAddressingModeNames[lod.TexAddrModeName]; } else { Trace.TraceWarning("Skipped unknown texture addressing mode {1} in shape {0}", lod.Name, lod.TexAddrModeName); } if (lod.AlphaTestMode == 1) { options |= SceneryMaterialOptions.AlphaTest; } if (ShaderNames.ContainsKey(lod.ShaderName)) { options |= ShaderNames[lod.ShaderName]; } else { Trace.TraceWarning("Skipped unknown shader name {1} in shape {0}", lod.Name, lod.ShaderName); } if (LightingModelNames.ContainsKey(lod.LightModelName)) { options |= LightingModelNames[lod.LightModelName]; } else { Trace.TraceWarning("Skipped unknown lighting model index {1} in shape {0}", lod.Name, lod.LightModelName); } if ((lod.ESD_Alternative_Texture & (int)TextureFlags.Night) != 0) { options |= SceneryMaterialOptions.NightTexture; } return(options); } // end EncodeMaterialOptions
/// <summary> /// Builds a SuperElevation LOD to SuperElevationProfile specifications as one vertex buffer and one index buffer. /// The order in which the buffers are built reflects the nesting in the TrProfile. The nesting order is: /// (Polylines (Vertices)). All vertices and indices are built contiguously for an LOD. /// </summary> /// <param name="viewer">Viewer.</param> /// <param name="worldPosition">WorldPosition.</param> /// <param name="iLOD">Index of LOD mesh to be generated from profile.</param> /// <param name="iLODItem">Index of LOD mesh to be generated from profile.</param> public new ShapePrimitive BuildPrimitive(Viewer viewer, WorldPosition worldPosition, int iLOD, int iLODItem) { // Call for track section to initialize itself if (DTrackData.IsCurved == 0) { LinearGen(); } else { CircArcGen(); } // Count vertices and indices LOD lod = (LOD)TrProfile.LODs[iLOD]; LODItem lodItem = (LODItem)lod.LODItems[iLODItem]; NumVertices = (int)(lodItem.NumVertices * (NumSections + 1)); NumIndices = (short)(lodItem.NumSegments * NumSections * 6); // (Cells x 2 triangles/cell x 3 indices/triangle) // Allocate memory for vertices and indices VertexList = new VertexPositionNormalTexture[NumVertices]; // numVertices is now aggregate TriangleListIndices = new short[NumIndices]; // as is NumIndices // Build the mesh for lod VertexIndex = 0; IndexIndex = 0; whichCase = 0; //0: no elevation (MaxElev=0), 1: start (startE = 0, Max!=end), //2: end (end=0, max!=start), 3: middle (start>0, end>0), 4: start and finish in one if (StartElev.AlmostEqual(0f, 0.001f) && MaxElev.AlmostEqual(0f, 0.001f) && EndElv.AlmostEqual(0f, 0.001f)) { whichCase = 0; //no elev } else if (StartElev.AlmostEqual(0f, 0.001f) && EndElv.AlmostEqual(0f, 0.001f)) { whichCase = 4; //finish/start in one } else if (StartElev.AlmostEqual(0f, 0.001f) && !EndElv.AlmostEqual(0f, 0.001f)) { whichCase = 1; //start } else if (EndElv.AlmostEqual(0f, 0.001f) && !StartElev.AlmostEqual(0f, 0.001f)) { whichCase = 2; //finish } else { whichCase = 3; //in middle } Matrix PreRotation = Matrix.Identity; elevated = MaxElev; if (whichCase == 3 || whichCase == 2) { PreRotation = Matrix.CreateRotationZ(-elevated * Math.Sign(DTrackData.param1)); } //if section is in the middle of curve, will only rotate the first set of vertex, others will follow the same rotation prevRotation = 0f; Vector3 tmp; // Initial load of baseline cross section polylines for this LOD only: foreach (Polyline pl in lodItem.Polylines) { foreach (Vertex v in pl.Vertices) { tmp = new Vector3(v.Position.X, v.Position.Y, v.Position.Z); if (whichCase == 3 || whichCase == 2) { tmp = Vector3.Transform(tmp, PreRotation); prevRotation = MaxElev; } VertexList[VertexIndex].Position = tmp; VertexList[VertexIndex].Normal = v.Normal; VertexList[VertexIndex].TextureCoordinate = v.TexCoord; VertexIndex++; } } // Initial load of base cross section complete // Now generate and load subsequent cross sections OldRadius = -center; uint stride = VertexIndex; offSet = 0; for (uint i = 0; i < NumSections; i++) { currentRotation = determineRotation(DTrackData.param1); elevated = currentRotation - prevRotation; prevRotation = currentRotation; if (DTrackData.param1 > 0) { elevated *= -1; } foreach (Polyline pl in lodItem.Polylines) { uint plv = 0; // Polyline vertex index foreach (Vertex v in pl.Vertices) { if (DTrackData.IsCurved == 0) { LinearGen(stride, pl); // Generation call } else { CircArcGen(stride, pl); } if (plv > 0) { // Sense for triangles is clockwise // First triangle: TriangleListIndices[IndexIndex++] = (short)VertexIndex; TriangleListIndices[IndexIndex++] = (short)(VertexIndex - 1 - stride); TriangleListIndices[IndexIndex++] = (short)(VertexIndex - 1); // Second triangle: TriangleListIndices[IndexIndex++] = (short)VertexIndex; TriangleListIndices[IndexIndex++] = (short)(VertexIndex - stride); TriangleListIndices[IndexIndex++] = (short)(VertexIndex - 1 - stride); } VertexIndex++; plv++; } } OldRadius = radius; // Get ready for next segment offSet++; } // Create and populate a new ShapePrimitive var indexBuffer = new IndexBuffer(viewer.GraphicsDevice, typeof(short), NumIndices, BufferUsage.WriteOnly); indexBuffer.SetData(TriangleListIndices); return(new ShapePrimitive(lodItem.LODMaterial, new SharedShape.VertexBufferSet(VertexList, viewer.GraphicsDevice), indexBuffer, 0, NumVertices, NumIndices / 3, new[] { -1 }, 0)); }
/// <summary> /// WireProfile constructor (default - builds from self-contained data) /// </summary> public WireProfile(Viewer viewer) // Nasty: void return type is not allowed. (See MSDN for compiler error CS0542.) : base(viewer, 0) //call the dummy base constructor so that no data is pre-populated { LODMethod = LODMethods.ComponentAdditive; LODWire lod; // Local LOD instance LODItemWire lodItem; // Local LODItem instance Polyline pl; // Local polyline instance Polyline vertical; expectedSegmentLength = 40; //segment of wire is expected to be 40 meters lod = new LODWire(800.0f); // Create LOD for railsides with specified CutoffRadius lodItem = new LODItemWire("Wire"); if (File.Exists(viewer.Simulator.RoutePath + "\\Textures\\overheadwire.ace")) { lodItem.TexName = "overheadwire.ace"; } else if (File.Exists(viewer.Simulator.BasePath + "\\global\\textures\\overheadwire.ace")) { lodItem.TexName = "..\\..\\..\\global\\textures\\overheadwire.ace"; } else { Trace.TraceInformation("Ignored missing overheadwire.ace, using default. You can copy the overheadwire.ace from OR\'s AddOns folder to {0}\\Textures", viewer.Simulator.RoutePath); lodItem.TexName = "..\\..\\..\\global\\textures\\dieselsmoke.ace"; } lodItem.ShaderName = "TexDiff"; lodItem.LightModelName = "DarkShade"; lodItem.AlphaTestMode = 0; lodItem.TexAddrModeName = "Wrap"; lodItem.ESD_Alternative_Texture = 0; lodItem.MipMapLevelOfDetailBias = 0; LODItem.LoadMaterial(viewer, lodItem); bool drawTriphaseWire = (viewer.Simulator.TRK.Tr_RouteFile.TriphaseEnabled == "Off" ? false : viewer.Simulator.TRK.Tr_RouteFile.TriphaseEnabled == "On"); bool drawDoubleWire = (viewer.Simulator.TRK.Tr_RouteFile.DoubleWireEnabled == "Off" ? false : viewer.Simulator.TRK.Tr_RouteFile.DoubleWireEnabled == "On" || viewer.Settings.DoubleWire); float topHeight = (float)viewer.Simulator.TRK.Tr_RouteFile.OverheadWireHeight; float topWireOffset = (viewer.Simulator.TRK.Tr_RouteFile.DoubleWireHeight > 0 ? viewer.Simulator.TRK.Tr_RouteFile.DoubleWireHeight : 1.0f); float dist = (viewer.Simulator.TRK.Tr_RouteFile.TriphaseWidth > 0 ? viewer.Simulator.TRK.Tr_RouteFile.TriphaseWidth : 1.0f); if (drawTriphaseWire) { pl = SingleWireProfile("TopWireLeft", topHeight, -dist / 2); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); pl = SingleWireProfile("TopWireRight", topHeight, dist / 2); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); } else { pl = SingleWireProfile("TopWire", topHeight); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); } if (drawDoubleWire) { topHeight += topWireOffset; if (drawTriphaseWire) { pl = SingleWireProfile("TopWire1Left", topHeight, -dist / 2); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); pl = SingleWireProfile("TopWire1Right", topHeight, dist / 2); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); vertical = VerticalWireProfile("TopWireVerticalLeft", topHeight, -dist / 2); lodItem.VerticalPolylines = new ArrayList(); lodItem.VerticalPolylines.Add(vertical); lodItem.VerticalAccumulate(vertical.Vertices.Count); vertical = VerticalWireProfile("TopWireVerticalRight", topHeight, dist / 2); lodItem.VerticalPolylines.Add(vertical); lodItem.VerticalAccumulate(vertical.Vertices.Count); } else { pl = SingleWireProfile("TopWire1", topHeight); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); vertical = VerticalWireProfile("TopWireVertical", topHeight); lodItem.VerticalPolylines = new ArrayList(); lodItem.VerticalPolylines.Add(vertical); lodItem.VerticalAccumulate(vertical.Vertices.Count); } } lod.LODItems.Add(lodItem); // Append to LODItems array base.LODs.Add(lod); // Append this lod to LODs array }
/// <summary> /// WireProfile constructor (default - builds from self-contained data) /// </summary> public WireProfile(Viewer viewer) // Nasty: void return type is not allowed. (See MSDN for compiler error CS0542.) : base(viewer, 0) //call the dummy base constructor so that no data is pre-populated { LODMethod = LODMethods.ComponentAdditive; LODWire lod; // Local LOD instance LODItemWire lodItem; // Local LODItem instance Polyline pl; // Local polyline instance Polyline vertical; expectedSegmentLength = 40; //segment of wire is expected to be 40 meters lod = new LODWire(800.0f); // Create LOD for railsides with specified CutoffRadius lodItem = new LODItemWire("Wire"); var normalvalue = 0.707f; if (File.Exists(viewer.Simulator.RoutePath + "\\Textures\\overheadwire.ace")) { lodItem.TexName = "overheadwire.ace"; } else { Trace.TraceInformation("Ignored missing overheadwire.ace, using default. You can copy the overheadwire.ace from OR\'s AddOns folder to {0}\\Textures", viewer.Simulator.RoutePath); lodItem.TexName = "..\\..\\..\\global\\textures\\dieselsmoke.ace"; } lodItem.ShaderName = "TexDiff"; lodItem.LightModelName = "OptSpecular0"; lodItem.AlphaTestMode = 0; lodItem.TexAddrModeName = "Wrap"; lodItem.ESD_Alternative_Texture = 0; lodItem.MipMapLevelOfDetailBias = 0; LODItem.LoadMaterial(viewer, lodItem); float topHeight = (float)viewer.Simulator.TRK.Tr_RouteFile.OverheadWireHeight; float u1 = 0.25f, v1 = 0.25f; pl = new Polyline(this, "TopWire", 5); pl.DeltaTexCoord = new Vector2(0.00f, 0.00f); pl.Vertices.Add(new Vertex(-0.01f, topHeight + 0.02f, 0.0f, -normalvalue, normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(0.01f, topHeight + 0.02f, 0.0f, normalvalue, normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(0.01f, topHeight, 0.0f, normalvalue, -normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(-0.01f, topHeight, 0.0f, -normalvalue, -normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(-0.01f, topHeight + 0.02f, 0.0f, -normalvalue, normalvalue, 0f, u1, v1)); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); if (viewer.Settings.DoubleWire) { pl = new Polyline(this, "TopWire1", 5); pl.DeltaTexCoord = new Vector2(0.00f, 0.00f); topHeight += 1.0f; pl.Vertices.Add(new Vertex(-0.01f, topHeight + 0.02f, 0.0f, -normalvalue, normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(0.01f, topHeight + 0.02f, 0.0f, normalvalue, normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(0.01f, topHeight, 0.0f, normalvalue, -normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(-0.01f, topHeight, 0.0f, -normalvalue, -normalvalue, 0f, u1, v1)); pl.Vertices.Add(new Vertex(-0.01f, topHeight + 0.02f, 0.0f, -normalvalue, normalvalue, 0f, u1, v1)); lodItem.Polylines.Add(pl); lodItem.Accum(pl.Vertices.Count); vertical = new Polyline(this, "TopWireVertical", 5); vertical.DeltaTexCoord = new Vector2(0.00f, 0.00f); vertical.Vertices.Add(new Vertex(-0.008f, topHeight, 0.008f, -normalvalue, 0f, normalvalue, u1, v1)); vertical.Vertices.Add(new Vertex(-.008f, topHeight, -.008f, normalvalue, 0f, normalvalue, u1, v1)); vertical.Vertices.Add(new Vertex(.008f, topHeight, -.008f, normalvalue, 0f, -normalvalue, u1, v1)); vertical.Vertices.Add(new Vertex(.008f, topHeight, .008f, -normalvalue, 0f, -normalvalue, u1, v1)); vertical.Vertices.Add(new Vertex(-.008f, topHeight, .008f, -normalvalue, 0f, normalvalue, u1, v1)); lodItem.VerticalPolylines = new ArrayList(); lodItem.VerticalPolylines.Add(vertical); lodItem.VerticalAccumulate(vertical.Vertices.Count); } lod.LODItems.Add(lodItem); // Append to LODItems array base.LODs.Add(lod); // Append this lod to LODs array }
private void Update() { GetComponentsInChildren <LODItem>(items); var camera = Camera.main; counter += 1; var pingThisFrame = false; if (counter % 10 == 0) { counter = 0; if (drawDebug) { pingThisFrame = true; } } var selector = GetComponent <PullTabSelector>(); var closestAngle = float.PositiveInfinity; LODItem closestItem = null; foreach (var item in items) { var testAngle = Vector3.Angle(camera.transform.forward, item.transform.position - camera.transform.position); var testDist = Vector3.Distance(item.transform.position, camera.transform.position); if (pingThisFrame) { DebugPing.Ping(item.transform.position, LeapColor.blue, 0.08f); Debug.Log(testAngle); } if (testAngle < closestAngle && testAngle <= maxCameraLookAngle && testDist <= maxDetailDistance) { closestAngle = testAngle; if (selector != null && selector.listOpenCloseAmount < 0.10f) { var activeMarbleItem = selector.activeMarbleParent.GetComponentInChildren <LODItem>(); if (item == activeMarbleItem) { closestItem = item; } } else { closestItem = item; } if (pingThisFrame) { DebugPing.Ping(item.transform.position, LeapColor.red, 0.09f); } } } if (viewMode == ViewMode.Full) { foreach (var item in items) { if (closestItem != null) { if (item.propertySwitch.GetIsOffOrTurningOff()) { item.propertySwitch.On(); } } else { if (item.propertySwitch.GetIsOnOrTurningOn()) { item.propertySwitch.Off(); } } } } else if (viewMode == ViewMode.Fade) { foreach (var item in items) { var detailActivation = 0f; if (closestItem != null) { float dist; switch (fadeDistanceMode) { case FadeDistanceMode.FastFalloff: dist = Mathf.Sqrt((item.transform.position - closestItem.transform.position).magnitude); detailActivation = dist.Map(0f, Mathf.Sqrt(fadeRadius), 1f, 0f); break; case FadeDistanceMode.SlowFalloff: dist = (item.transform.position - closestItem.transform.position).sqrMagnitude; detailActivation = dist.Map(0f, fadeRadius * fadeRadius, 1f, 0f); break; case FadeDistanceMode.Linear: default: dist = (item.transform.position - closestItem.transform.position).magnitude; detailActivation = dist.Map(0f, fadeRadius, 1f, 0f); break; } if (detailActivation < cutoffActivationPercent) { detailActivation = 0f; } } if (item.tweenSwitch != null) { item.tweenSwitch.SetTweenTarget(detailActivation); } else { Debug.LogError("Can't use Fade view mode for this item; it doesn't use a " + "TweenSwitch.", item); } } } else if (viewMode == ViewMode.Single) { foreach (var item in items) { if (item != closestItem && item.tweenSwitch != null && item.propertySwitch.GetIsOnOrTurningOn()) { item.propertySwitch.Off(); } } // Older: one at a time; if (closestItem != null && closestItem.tweenSwitch != null && closestItem.propertySwitch.GetIsOffOrTurningOff()) { closestItem.propertySwitch.On(); } } if (pingThisFrame && closestItem != null) { DebugPing.Ping(closestItem.transform.position, LeapColor.purple, 0.10f); } }