diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5740f02cd..2f50f6ae1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,10 @@
Built with Unity 2020.3.
### Added
+- Support for dynamic wires, also known as *Fast Flip* ([#330](https://github.com/freezy/VisualPinball.Engine/pull/330), [Documentation](https://docs.visualpinball.org/creators-guide/editor/wire-manager.html#dynamic)).
+- Component for light groups, allowing easy grouping of GI lamps. ([#330](https://github.com/freezy/VisualPinball.Engine/pull/330) ([Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/light-groups.html))).
- Slingshot component ([#329](https://github.com/freezy/VisualPinball.Engine/pull/329), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/slingshots.html)).
-- Create insert meshes ([#320](https://github.com/freezy/VisualPinball.Engine/pull/320))
+- Create insert meshes ([#320](https://github.com/freezy/VisualPinball.Engine/pull/320)).
- Full support for custom playfield meshes.
- Remove Hybrid Renderer ([#316](https://github.com/freezy/VisualPinball.Engine/pull/316)).
- Create and use Unity assets when importing ([#320](https://github.com/freezy/VisualPinball.Engine/pull/302)).
@@ -37,6 +39,7 @@ Built with Unity 2020.3.
- Put game-, mesh-, collision- animation data into separate components ([#227](https://github.com/freezy/VisualPinball.Engine/pull/227), [Documentation](https://docs.visualpinball.org/creators-guide/editor/unity-components.html)).
### Fixed
+- Lighting setup. It's now usable ([#330](https://github.com/freezy/VisualPinball.Engine/pull/330)).
- Ball passing through collider plane and disappearing.
- Alpha channel of color values is now correctly written ([#291](https://github.com/freezy/VisualPinball.Engine/pull/291)).
- Layer names are correctly computed when importing a 10.6 file ([#291](https://github.com/freezy/VisualPinball.Engine/pull/291)).
diff --git a/VisualPinball.Engine/Game/Engines/GamelogicEngineCoil.cs b/VisualPinball.Engine/Game/Engines/GamelogicEngineCoil.cs
index 9b3aa916c..6ddae5e92 100644
--- a/VisualPinball.Engine/Game/Engines/GamelogicEngineCoil.cs
+++ b/VisualPinball.Engine/Game/Engines/GamelogicEngineCoil.cs
@@ -27,8 +27,8 @@ public class GamelogicEngineCoil : IGamelogicEngineDeviceItem
public string Description { get => _description; set => _description = value; }
public int InternalId;
- public string DeviceHint;
- public string DeviceItemHint;
+ public string DeviceHint { get => _deviceHint; set => _deviceHint = value; }
+ public string DeviceItemHint { get => _deviceItemHint; set => _deviceItemHint = value; }
public bool IsLamp;
///
@@ -38,6 +38,8 @@ public class GamelogicEngineCoil : IGamelogicEngineDeviceItem
private string _description;
private string _id;
+ private string _deviceHint;
+ private string _deviceItemHint;
public GamelogicEngineCoil(string id)
{
diff --git a/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs b/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs
index 3b63f363b..732faa1ce 100644
--- a/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs
+++ b/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs
@@ -17,30 +17,78 @@
// ReSharper disable InconsistentNaming
using System;
+using VisualPinball.Engine.Math;
namespace VisualPinball.Engine.Game.Engines
{
[Serializable]
public class GamelogicEngineLamp : IGamelogicEngineDeviceItem
{
- public string Id { get; set; }
- public int InternalId;
- public string Description { get; set; }
- public string DeviceHint;
- public string DeviceItemHint;
- public string MainLampIdOfGreen;
- public string MainLampIdOfBlue;
-
- public GamelogicEngineLamp(string id)
- {
- Id = id;
- InternalId = int.TryParse(id, out var internalId) ? internalId : 0;
+ ///
+ /// The unique ID of this lamp, as the gamelogic engine addresses it.
+ ///
+ public string Id { get => _id; set => _id = value; }
+
+ ///
+ /// Some gamelogic engines use integers for the ID. In order to avoid repetitive casting, we store it as integer as well.
+ ///
+ public int InternalId;
+
+ ///
+ /// Which channel this lamp corresponds to.
+ ///
+ public ColorChannel Channel = ColorChannel.Alpha;
+
+ ///
+ /// If it's a fading light, this is the value at maximal intensity.
+ ///
+ public int FadingSteps;
+
+ ///
+ /// An optional description of the lamp.
+ ///
+ public string Description { get => _description; set => _description = value; }
+
+ ///
+ /// How the gamelogic engine triggers the lamp. Either through GI or through the normal lamp API.
+ ///
+ ///
+ ///
+ /// Note that lamps connected to coils will appear under coils.
+ ///
+ public LampSource Source = LampSource.Lamp;
+
+ ///
+ /// A regular expression to match the component on the playfield.
+ ///
+ public string DeviceHint { get => _deviceHint; set => _deviceHint = value; }
+
+ ///
+ /// A regular expression to match the lamp component within the component.
+ ///
+ public string DeviceItemHint { get => _deviceItemHint; set => _deviceItemHint = value; }
+
+ private string _description;
+ private string _id;
+ private string _deviceHint;
+ private string _deviceItemHint;
+
+ public GamelogicEngineLamp(string id)
+ {
+ Id = id;
+ InternalId = int.TryParse(id, out var internalId) ? internalId : 0;
+ }
+
+ public GamelogicEngineLamp(string id, int internalId)
+ {
+ Id = id;
+ InternalId = internalId;
+ }
}
- public GamelogicEngineLamp(string id, int internalId)
+ public enum LampSource
{
- Id = id;
- InternalId = internalId;
- }
+ Lamp = 0,
+ GI = 1,
}
}
diff --git a/VisualPinball.Engine/Game/Engines/GamelogicEngineSwitch.cs b/VisualPinball.Engine/Game/Engines/GamelogicEngineSwitch.cs
index 5697adb3b..f88d61a37 100644
--- a/VisualPinball.Engine/Game/Engines/GamelogicEngineSwitch.cs
+++ b/VisualPinball.Engine/Game/Engines/GamelogicEngineSwitch.cs
@@ -61,12 +61,15 @@ public class GamelogicEngineSwitch : IGamelogicEngineDeviceItem
public string Description { get => _description; set => _description = value; }
public string InputActionHint;
public string InputMapHint;
- public string DeviceHint;
- public string DeviceItemHint;
+
+ public string DeviceHint { get => _deviceHint; set => _deviceHint = value; }
+ public string DeviceItemHint { get => _deviceItemHint; set => _deviceItemHint = value; }
public SwitchConstantHint ConstantHint = SwitchConstantHint.None;
private string _description;
private string _id;
+ private string _deviceHint;
+ private string _deviceItemHint;
public GamelogicEngineSwitch(string id)
{
diff --git a/VisualPinball.Engine/Game/Engines/GamelogicEngineWire.cs b/VisualPinball.Engine/Game/Engines/GamelogicEngineWire.cs
new file mode 100644
index 000000000..2dd4af532
--- /dev/null
+++ b/VisualPinball.Engine/Game/Engines/GamelogicEngineWire.cs
@@ -0,0 +1,44 @@
+// Visual Pinball Engine
+// Copyright (C) 2021 freezy and VPE Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// ReSharper disable InconsistentNaming
+
+using System;
+
+namespace VisualPinball.Engine.Game.Engines
+{
+ [Serializable]
+ public class GamelogicEngineWire
+ {
+ public string Description;
+ public string SourceId;
+ public string DestinationId;
+ public DestinationType DestinationType;
+
+ public GamelogicEngineWire(string srcId, string destId, DestinationType type, string description = "")
+ {
+ Description = description;
+ SourceId = srcId;
+ DestinationId = destId;
+ DestinationType = type;
+ }
+ }
+
+ public enum DestinationType
+ {
+ Coil, Lamp, GI
+ }
+}
diff --git a/VisualPinball.Engine/Game/Engines/GamelogicEngineWire.cs.meta b/VisualPinball.Engine/Game/Engines/GamelogicEngineWire.cs.meta
new file mode 100644
index 000000000..cf4af47a7
--- /dev/null
+++ b/VisualPinball.Engine/Game/Engines/GamelogicEngineWire.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 008c0b52a3b92064c8088f04a57f300c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Engine/Game/Engines/IGamelogicEngineDeviceItem.cs b/VisualPinball.Engine/Game/Engines/IGamelogicEngineDeviceItem.cs
index 4825356ee..2cd6da680 100644
--- a/VisualPinball.Engine/Game/Engines/IGamelogicEngineDeviceItem.cs
+++ b/VisualPinball.Engine/Game/Engines/IGamelogicEngineDeviceItem.cs
@@ -20,5 +20,7 @@ public interface IGamelogicEngineDeviceItem
{
string Id { get; }
string Description { get; }
+ string DeviceHint { get; }
+ string DeviceItemHint { get; }
}
}
diff --git a/VisualPinball.Engine/Math/Color.cs b/VisualPinball.Engine/Math/Color.cs
index 0e6025f75..88df01e34 100644
--- a/VisualPinball.Engine/Math/Color.cs
+++ b/VisualPinball.Engine/Math/Color.cs
@@ -98,7 +98,25 @@ public enum ColorFormat
public enum ColorChannel
{
- Red, Green, Blue, Alpha
+ ///
+ /// The red channel for RGB lamps.
+ ///
+ Red,
+
+ ///
+ /// The green channel for RGB lamps.
+ ///
+ Green,
+
+ ///
+ /// The blue channel for RBG lamps.
+ ///
+ Blue,
+
+ ///
+ /// The intensity for white lamps.
+ ///
+ Alpha,
}
public static class Colors
diff --git a/VisualPinball.Engine/VPT/Light/Light.cs b/VisualPinball.Engine/VPT/Light/Light.cs
index 3da93a154..cf16973e7 100644
--- a/VisualPinball.Engine/VPT/Light/Light.cs
+++ b/VisualPinball.Engine/VPT/Light/Light.cs
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+using System;
using System.IO;
using VisualPinball.Engine.Game;
using VisualPinball.Engine.Math;
@@ -29,6 +30,8 @@ public class Light : Item, IRenderable
private readonly LightMeshGenerator _meshGenerator;
+ public bool IsInsertLight(Table.Table table) => !Data.ShowBulbMesh && string.Equals(Data.OffImage, table.Data.Image, StringComparison.OrdinalIgnoreCase);
+
public Light(LightData data) : base(data)
{
_meshGenerator = new LightMeshGenerator(Data);
@@ -38,11 +41,9 @@ public Light(BinaryReader reader, string itemName) : this(new LightData(reader,
{
}
- public static Light GetDefault(Table.Table table)
+ public static Light GetDefault(string name, float x, float y)
{
- var x = table.Width / 2f;
- var y = table.Height / 2f;
- var lightData = new LightData(table.GetNewName("Light"), table.Width / 2f, table.Height / 2f) {
+ var lightData = new LightData(name, x, y) {
DragPoints = new[] {
new DragPointData(x, y - 50f) {IsSmooth = true },
new DragPointData(x - 50f * MathF.Cos(MathF.PI / 4), y - 50f * MathF.Sin(MathF.PI / 4)) {IsSmooth = true },
@@ -56,6 +57,8 @@ public static Light GetDefault(Table.Table table)
};
return new Light(lightData);
}
+
+ public static Light GetDefault(Table.Table table) => GetDefault(table.GetNewName("Light"), table.Width / 2f, table.Height / 2f);
#region IRenderable
diff --git a/VisualPinball.Engine/VPT/Material.cs b/VisualPinball.Engine/VPT/Material.cs
index 0fad356e9..5f18d0be8 100644
--- a/VisualPinball.Engine/VPT/Material.cs
+++ b/VisualPinball.Engine/VPT/Material.cs
@@ -16,6 +16,7 @@
#region ReSharper
// ReSharper disable FieldCanBeMadeReadOnly.Global
+// ReSharper disable InconsistentNaming
#endregion
using System;
@@ -140,6 +141,7 @@ public Material(string name) : this()
BaseColor = new Color(255, 255, 255, 255);
Glossiness = new Color(0, 0, 0, 255);
ClearCoat = new Color(0, 0, 0, 255);
+ Opacity = 1f;
UpdateData();
}
diff --git a/VisualPinball.Engine/VPT/PbrMaterial.cs b/VisualPinball.Engine/VPT/PbrMaterial.cs
index b62eff922..c77529d8c 100644
--- a/VisualPinball.Engine/VPT/PbrMaterial.cs
+++ b/VisualPinball.Engine/VPT/PbrMaterial.cs
@@ -39,6 +39,10 @@ public class PbrMaterial
public bool IsOpacityActive => _material?.IsOpacityActive ?? false;
public float Opacity => MathF.Min(1, MathF.Max(0, _material?.Opacity ?? 1f));
public float Roughness => _material?.Roughness ?? 0.5f;
+
+ public MaterialType MaterialType = MaterialType.Translucent;
+ public DiffusionProfileTemplate DiffusionProfile = DiffusionProfileTemplate.None;
+
private float Edge => _material?.Edge ?? 0f;
public const string NameNoMaterial = "__std";
@@ -155,4 +159,20 @@ public enum BlendMode
Cutout,
Translucent
}
+
+ public enum DiffusionProfileTemplate
+ {
+ None,
+ Plastics
+ }
+
+ public enum MaterialType
+ {
+ SubsurfaceScattering = 0,
+ Standard = 1,
+ Anisotropy = 2,
+ Iridescence = 3,
+ SpecularColor = 4,
+ Translucent = 5,
+ }
}
diff --git a/VisualPinball.Engine/VPT/Table/TableMeshGenerator.cs b/VisualPinball.Engine/VPT/Table/TableMeshGenerator.cs
index b3347c1ff..9a712325e 100644
--- a/VisualPinball.Engine/VPT/Table/TableMeshGenerator.cs
+++ b/VisualPinball.Engine/VPT/Table/TableMeshGenerator.cs
@@ -45,7 +45,9 @@ public Mesh GetMesh(Table table, bool asRightHanded = true)
public PbrMaterial GetMaterial(Table table)
{
- return new PbrMaterial(table.GetMaterial(_data.PlayfieldMaterial), table.GetTexture(_data.Image));
+ return new PbrMaterial(table.GetMaterial(_data.PlayfieldMaterial), table.GetTexture(_data.Image)) {
+ MaterialType = MaterialType.Standard
+ };
}
public void SetFromPrimitive(Primitive.Primitive primitive)
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_flasher.png
new file mode 100644
index 000000000..cbc5bfed5
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_flasher.png.meta
new file mode 100644
index 000000000..692ef0196
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: e6e248795a18ee047a379e32da191a98
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_group.png
new file mode 100644
index 000000000..70b1d144d
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_group.png.meta
new file mode 100644
index 000000000..ca0e3df3e
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 6a45ecf4c0ab9bb4a9e22f42cf23baeb
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_flasher.png
new file mode 100644
index 000000000..4ff7f984b
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_flasher.png.meta
new file mode 100644
index 000000000..fe8c507ea
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: a40b3d2a10078044d89e53806008c529
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_group.png
new file mode 100644
index 000000000..0dc0e9073
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_group.png.meta
new file mode 100644
index 000000000..aae769e0e
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 4a2d5ab0736f430459492a0d0a9f685d
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_flasher.png
new file mode 100644
index 000000000..5eb92c177
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_flasher.png.meta
new file mode 100644
index 000000000..9b3e3740d
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 34b11994d23f3534e949a1f4ecbbe740
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_group.png
new file mode 100644
index 000000000..16b532407
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_group.png.meta
new file mode 100644
index 000000000..b8d970966
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: c46067de66c404f44abe1c701ae0c665
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_flasher.png
new file mode 100644
index 000000000..81aaf8803
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_flasher.png.meta
new file mode 100644
index 000000000..e0b4e3d04
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: b3f3991fcaffa5b40a6de9c905d49cb6
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_group.png
new file mode 100644
index 000000000..5be9ae1af
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_group.png.meta
new file mode 100644
index 000000000..71be46dce
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: fbc38e515316e10469ebe7069403cf73
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 512
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_flasher.png
new file mode 100644
index 000000000..4b9a76255
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_flasher.png.meta
new file mode 100644
index 000000000..2919386e5
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: cb303c2839675e640b3c87ce45537e6d
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_group.png
new file mode 100644
index 000000000..76a3328a3
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_group.png.meta
new file mode 100644
index 000000000..4f9711e30
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 68aff253cda433f4cac498b99c5dabd0
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_flasher.png
new file mode 100644
index 000000000..a824e7f9d
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_flasher.png.meta
new file mode 100644
index 000000000..115099b20
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 29c771ace0eb9234d90160c29edb4153
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_group.png
new file mode 100644
index 000000000..cc3c5feac
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_group.png.meta
new file mode 100644
index 000000000..ac00ac922
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 29b231382b76fca4e8e15c20e58fdaba
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_flasher.png
new file mode 100644
index 000000000..7d414e442
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_flasher.png.meta
new file mode 100644
index 000000000..d52d9a093
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: dae644413301ce641bfb313e13d9b2e5
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_group.png
new file mode 100644
index 000000000..4b6d480fc
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_group.png.meta
new file mode 100644
index 000000000..1b50c7d9d
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_green/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 5c0bc5111675cea41ba8e8feff34ceae
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_flasher.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_flasher.png
new file mode 100644
index 000000000..d43c9b6d6
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_flasher.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_flasher.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_flasher.png.meta
new file mode 100644
index 000000000..fc7d35629
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_flasher.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 03496ff5246298144b02339367e6e448
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_group.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_group.png
new file mode 100644
index 000000000..f54371888
Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_group.png differ
diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_group.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_group.png.meta
new file mode 100644
index 000000000..9b8d361d7
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/light_group.png.meta
@@ -0,0 +1,108 @@
+fileFormatVersion: 2
+guid: 8556b8d36238d3945ab0b87bf995aa62
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 2
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 64
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab b/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab
index 9e8f3718f..31b06c212 100644
--- a/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab
+++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab
@@ -9,8 +9,7 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 2649789736405374282}
- - component: {fileID: -7726742479185607768}
- - component: {fileID: 8134916103503386669}
+ - component: {fileID: 4750229237398323307}
m_Layer: 0
m_Name: Light (Builtin)
m_TagString: Untagged
@@ -31,10 +30,11 @@ Transform:
m_Children:
- {fileID: 3878378365098062784}
- {fileID: 6343396272821131885}
+ - {fileID: 4222681214933490337}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!114 &-7726742479185607768
+--- !u!114 &4750229237398323307
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -58,68 +58,6 @@ MonoBehaviour:
BlinkInterval: 0
FadeSpeedUp: 0
FadeSpeedDown: 0
---- !u!108 &8134916103503386669
-Light:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 550864702806823459}
- m_Enabled: 1
- serializedVersion: 10
- m_Type: 2
- m_Shape: 0
- m_Color: {r: 1, g: 1, b: 1, a: 1}
- m_Intensity: 47.746483
- m_Range: 10
- m_SpotAngle: 30
- m_InnerSpotAngle: 21.80208
- m_CookieSize: 10
- m_Shadows:
- m_Type: 0
- m_Resolution: -1
- m_CustomResolution: -1
- m_Strength: 1
- m_Bias: 0.05
- m_NormalBias: 0.4
- m_NearPlane: 0.2
- m_CullingMatrixOverride:
- e00: 1
- e01: 0
- e02: 0
- e03: 0
- e10: 0
- e11: 1
- e12: 0
- e13: 0
- e20: 0
- e21: 0
- e22: 1
- e23: 0
- e30: 0
- e31: 0
- e32: 0
- e33: 1
- m_UseCullingMatrixOverride: 0
- m_Cookie: {fileID: 0}
- m_DrawHalo: 0
- m_Flare: {fileID: 0}
- m_RenderMode: 0
- m_CullingMask:
- serializedVersion: 2
- m_Bits: 4294967295
- m_RenderingLayerMask: 1
- m_Lightmapping: 4
- m_LightShadowCasterMode: 2
- m_AreaSize: {x: 1, y: 1}
- m_BounceIntensity: 1
- m_ColorTemperature: 6570
- m_UseColorTemperature: 1
- m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
- m_UseBoundingSphereOverride: 0
- m_UseViewFrustumForShadowCasterCull: 1
- m_ShadowRadius: 0
- m_ShadowAngle: 0
--- !u!1 &3112338884683058496
GameObject:
m_ObjectHideFlags: 0
@@ -147,7 +85,7 @@ Transform:
m_GameObject: {fileID: 3112338884683058496}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
- m_LocalScale: {x: 1, y: 1, z: 1}
+ m_LocalScale: {x: 20, y: 20, z: 20}
m_Children: []
m_Father: {fileID: 2649789736405374282}
m_RootOrder: 0
@@ -228,7 +166,7 @@ Transform:
m_GameObject: {fileID: 6207005947276562095}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
- m_LocalScale: {x: 1, y: 1, z: 1}
+ m_LocalScale: {x: 20, y: 20, z: 20}
m_Children: []
m_Father: {fileID: 2649789736405374282}
m_RootOrder: 1
@@ -282,3 +220,96 @@ MeshRenderer:
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &7031967719973800860
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4222681214933490337}
+ - component: {fileID: 1799189321192003275}
+ m_Layer: 0
+ m_Name: Source
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4222681214933490337
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7031967719973800860}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: -0.09030962, y: 0.026130915, z: 0.6205269}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 2649789736405374282}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!108 &1799189321192003275
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7031967719973800860}
+ m_Enabled: 1
+ serializedVersion: 10
+ m_Type: 2
+ m_Shape: 0
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_Intensity: 47.746483
+ m_Range: 10
+ m_SpotAngle: 30
+ m_InnerSpotAngle: 21.80208
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 0
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_CullingMatrixOverride:
+ e00: 1
+ e01: 0
+ e02: 0
+ e03: 0
+ e10: 0
+ e11: 1
+ e12: 0
+ e13: 0
+ e20: 0
+ e21: 0
+ e22: 1
+ e23: 0
+ e30: 0
+ e31: 0
+ e32: 0
+ e33: 1
+ m_UseCullingMatrixOverride: 0
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingLayerMask: 1
+ m_Lightmapping: 4
+ m_LightShadowCasterMode: 2
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 1
+ m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+ m_UseBoundingSphereOverride: 0
+ m_UseViewFrustumForShadowCasterCull: 1
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab.meta b/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab.meta
index 00e8b1d56..777e6675b 100644
--- a/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab.meta
+++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Light (Builtin).prefab.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 24c87dc2ea2263b48b6be1b03f90eb88
+guid: b689c951e6196b44aa743ee7e633846a
PrefabImporter:
externalObjects: {}
userData:
diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Light - Insert (Builtin).prefab b/VisualPinball.Unity/Assets/Resources/Prefabs/Light - Insert (Builtin).prefab
new file mode 100644
index 000000000..473957ee1
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Light - Insert (Builtin).prefab
@@ -0,0 +1,207 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &550864702806823459
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2649789736405374282}
+ - component: {fileID: 4750229237398323307}
+ m_Layer: 0
+ m_Name: Light - Insert (Builtin)
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2649789736405374282
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 550864702806823459}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 179995335602121591}
+ - {fileID: 4222681214933490337}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4750229237398323307
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 550864702806823459}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 9e53d77b7d585c94b89513d95571354e, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _isLocked: 0
+ _editorLayer: 0
+ _editorLayerName:
+ _editorLayerVisibility: 1
+ Position: {x: 0, y: 0, z: 0}
+ _surface: {fileID: 0}
+ BulbSize: 20
+ State: 0
+ BlinkPattern:
+ BlinkInterval: 0
+ FadeSpeedUp: 0
+ FadeSpeedDown: 0
+--- !u!1 &5987759878404274838
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 179995335602121591}
+ - component: {fileID: 4762606870770787496}
+ - component: {fileID: 5251209611439210537}
+ m_Layer: 0
+ m_Name: Insert
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &179995335602121591
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5987759878404274838}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: -0, y: -0, z: 0.1}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 2649789736405374282}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4762606870770787496
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5987759878404274838}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: e2a11a120b7da4b4db6fd330516311db, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ InsertHeight: 20
+ PositionZ: 0.1
+ _dragPoints: []
+--- !u!33 &5251209611439210537
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5987759878404274838}
+ m_Mesh: {fileID: 0}
+--- !u!1 &7031967719973800860
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4222681214933490337}
+ - component: {fileID: 1799189321192003275}
+ m_Layer: 0
+ m_Name: Source
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4222681214933490337
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7031967719973800860}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: -50}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 2649789736405374282}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!108 &1799189321192003275
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7031967719973800860}
+ m_Enabled: 1
+ serializedVersion: 10
+ m_Type: 0
+ m_Shape: 0
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_Intensity: 462.10742
+ m_Range: 0.1
+ m_SpotAngle: 75
+ m_InnerSpotAngle: 1
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 0
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_CullingMatrixOverride:
+ e00: 1
+ e01: 0
+ e02: 0
+ e03: 0
+ e10: 0
+ e11: 1
+ e12: 0
+ e13: 0
+ e20: 0
+ e21: 0
+ e22: 1
+ e23: 0
+ e30: 0
+ e31: 0
+ e32: 0
+ e33: 1
+ m_UseCullingMatrixOverride: 0
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingLayerMask: 1
+ m_Lightmapping: 4
+ m_LightShadowCasterMode: 2
+ m_AreaSize: {x: 0.5, y: 0.5}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 2700
+ m_UseColorTemperature: 1
+ m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+ m_UseBoundingSphereOverride: 0
+ m_UseViewFrustumForShadowCasterCull: 1
+ m_ShadowRadius: 0.01
+ m_ShadowAngle: 0
diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Light - Insert (Builtin).prefab.meta b/VisualPinball.Unity/Assets/Resources/Prefabs/Light - Insert (Builtin).prefab.meta
new file mode 100644
index 000000000..25e88cf22
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Light - Insert (Builtin).prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d51e304383510344c8e8029ec59eeebc
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Shaders/Srp/Display/DotMatrixDisplayGraph.shadergraph b/VisualPinball.Unity/Assets/Shaders/Srp/Display/DotMatrixDisplayGraph.shadergraph
index b90e6d284..258f5cb91 100644
--- a/VisualPinball.Unity/Assets/Shaders/Srp/Display/DotMatrixDisplayGraph.shadergraph
+++ b/VisualPinball.Unity/Assets/Shaders/Srp/Display/DotMatrixDisplayGraph.shadergraph
@@ -17,6 +17,9 @@
},
{
"m_Id": "355af990f31d4b91969f5a1d85c0e524"
+ },
+ {
+ "m_Id": "1fcaaabba6354a7ea01b55bbb16087d6"
}
],
"m_Keywords": [],
@@ -83,6 +86,15 @@
},
{
"m_Id": "3930f3e174464ad483bf5dfc32f941dd"
+ },
+ {
+ "m_Id": "c0ef6eda9aa241c089a8c97ea98ccd35"
+ },
+ {
+ "m_Id": "3e559fc89f98495daa87f9a9a83c3e6d"
+ },
+ {
+ "m_Id": "43b86669fec34ffebed1498171e47274"
}
],
"m_GroupDatas": [],
@@ -97,7 +109,21 @@
},
"m_InputSlot": {
"m_Node": {
- "m_Id": "5e3f39ce35b74ffcb64ad89380f95b94"
+ "m_Id": "3e559fc89f98495daa87f9a9a83c3e6d"
+ },
+ "m_SlotId": 1
+ }
+ },
+ {
+ "m_OutputSlot": {
+ "m_Node": {
+ "m_Id": "386fcbc47636417199e958b1ab89b4b7"
+ },
+ "m_SlotId": 0
+ },
+ "m_InputSlot": {
+ "m_Node": {
+ "m_Id": "c0ef6eda9aa241c089a8c97ea98ccd35"
},
"m_SlotId": 0
}
@@ -133,17 +159,45 @@
{
"m_OutputSlot": {
"m_Node": {
- "m_Id": "915771b37c834feb95561bee0773591f"
+ "m_Id": "3e559fc89f98495daa87f9a9a83c3e6d"
},
"m_SlotId": 0
},
"m_InputSlot": {
"m_Node": {
- "m_Id": "5989918dd1294dc48c01714e8dab3238"
+ "m_Id": "5e3f39ce35b74ffcb64ad89380f95b94"
},
"m_SlotId": 0
}
},
+ {
+ "m_OutputSlot": {
+ "m_Node": {
+ "m_Id": "43b86669fec34ffebed1498171e47274"
+ },
+ "m_SlotId": 0
+ },
+ "m_InputSlot": {
+ "m_Node": {
+ "m_Id": "3e559fc89f98495daa87f9a9a83c3e6d"
+ },
+ "m_SlotId": 2
+ }
+ },
+ {
+ "m_OutputSlot": {
+ "m_Node": {
+ "m_Id": "915771b37c834feb95561bee0773591f"
+ },
+ "m_SlotId": 0
+ },
+ "m_InputSlot": {
+ "m_Node": {
+ "m_Id": "c0ef6eda9aa241c089a8c97ea98ccd35"
+ },
+ "m_SlotId": 1
+ }
+ },
{
"m_OutputSlot": {
"m_Node": {
@@ -256,6 +310,20 @@
"m_SlotId": 3
}
},
+ {
+ "m_OutputSlot": {
+ "m_Node": {
+ "m_Id": "c0ef6eda9aa241c089a8c97ea98ccd35"
+ },
+ "m_SlotId": 2
+ },
+ "m_InputSlot": {
+ "m_Node": {
+ "m_Id": "5989918dd1294dc48c01714e8dab3238"
+ },
+ "m_SlotId": 0
+ }
+ },
{
"m_OutputSlot": {
"m_Node": {
@@ -315,8 +383,8 @@
],
"m_VertexContext": {
"m_Position": {
- "x": 10.66672134399414,
- "y": -948.6666259765625
+ "x": 154.66656494140626,
+ "y": -1156.0001220703125
},
"m_Blocks": [
{
@@ -332,8 +400,8 @@
},
"m_FragmentContext": {
"m_Position": {
- "x": 10.66672134399414,
- "y": -560.0
+ "x": 155.33334350585938,
+ "y": -629.3334350585938
},
"m_Blocks": [
{
@@ -513,6 +581,53 @@
"m_BareResource": false
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot",
+ "m_ObjectId": "1f7989ec260f4a26931027a955db33f0",
+ "m_Id": 0,
+ "m_DisplayName": "A",
+ "m_SlotType": 0,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "A",
+ "m_StageCapability": 3,
+ "m_Value": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "w": 0.0
+ },
+ "m_DefaultValue": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "w": 0.0
+ }
+}
+
+{
+ "m_SGVersion": 1,
+ "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty",
+ "m_ObjectId": "1fcaaabba6354a7ea01b55bbb16087d6",
+ "m_Guid": {
+ "m_GuidSerialized": "9acb2d53-46b0-4fad-a7c2-5414a709d865"
+ },
+ "m_Name": "Emission",
+ "m_DefaultReferenceName": "Vector1_1fcaaabba6354a7ea01b55bbb16087d6",
+ "m_OverrideReferenceName": "__Emission",
+ "m_GeneratePropertyBlock": true,
+ "m_Precision": 0,
+ "overrideHLSLDeclaration": false,
+ "hlslDeclarationOverride": 0,
+ "m_Hidden": false,
+ "m_Value": 1.0,
+ "m_FloatType": 0,
+ "m_RangeValues": {
+ "x": 0.0,
+ "y": 1.0
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot",
@@ -528,6 +643,21 @@
"m_Labels": []
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot",
+ "m_ObjectId": "322004d512c142f29eaaf7eb579ffc83",
+ "m_Id": 2,
+ "m_DisplayName": "Intensity",
+ "m_SlotType": 0,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "Intensity",
+ "m_StageCapability": 3,
+ "m_Value": 800.0,
+ "m_DefaultValue": 1.0,
+ "m_Labels": []
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.BlockNode",
@@ -644,9 +774,9 @@
"m_Expanded": true,
"m_Position": {
"serializedVersion": "2",
- "x": -444.6667785644531,
- "y": -948.6666870117188,
- "width": 214.6666717529297,
+ "x": -510.6667785644531,
+ "y": -958.0001220703125,
+ "width": 208.0,
"height": 374.0
}
},
@@ -743,6 +873,49 @@
"m_Labels": []
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.Rendering.HighDefinition.EmissionNode",
+ "m_ObjectId": "3e559fc89f98495daa87f9a9a83c3e6d",
+ "m_Group": {
+ "m_Id": ""
+ },
+ "m_Name": "Emission Node",
+ "m_DrawState": {
+ "m_Expanded": true,
+ "m_Position": {
+ "serializedVersion": "2",
+ "x": -171.33328247070313,
+ "y": -906.0000610351563,
+ "width": 229.3333282470703,
+ "height": 186.0
+ }
+ },
+ "m_Slots": [
+ {
+ "m_Id": "80c5b8e752414a589e157a082c1876f0"
+ },
+ {
+ "m_Id": "322004d512c142f29eaaf7eb579ffc83"
+ },
+ {
+ "m_Id": "751e5e2717a84b6e95e1d013224de5bf"
+ },
+ {
+ "m_Id": "a718235381784603bb6c139dea579167"
+ }
+ ],
+ "synonyms": [],
+ "m_Precision": 0,
+ "m_PreviewExpanded": true,
+ "m_PreviewMode": 0,
+ "m_CustomColors": {
+ "m_SerializableColors": []
+ },
+ "_intensityUnit": 0,
+ "m_NormalizeColor": false
+}
+
{
"m_SGVersion": 1,
"m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty",
@@ -766,6 +939,41 @@
}
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.PropertyNode",
+ "m_ObjectId": "43b86669fec34ffebed1498171e47274",
+ "m_Group": {
+ "m_Id": ""
+ },
+ "m_Name": "Property",
+ "m_DrawState": {
+ "m_Expanded": true,
+ "m_Position": {
+ "serializedVersion": "2",
+ "x": -1722.666748046875,
+ "y": -503.3332824707031,
+ "width": 125.33338165283203,
+ "height": 34.000003814697269
+ }
+ },
+ "m_Slots": [
+ {
+ "m_Id": "f15c0863146c4d569fb25b836a2cacc2"
+ }
+ ],
+ "synonyms": [],
+ "m_Precision": 0,
+ "m_PreviewExpanded": true,
+ "m_PreviewMode": 0,
+ "m_CustomColors": {
+ "m_SerializableColors": []
+ },
+ "m_Property": {
+ "m_Id": "1fcaaabba6354a7ea01b55bbb16087d6"
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.Texture2DInputMaterialSlot",
@@ -1083,6 +1291,21 @@
"m_Labels": []
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot",
+ "m_ObjectId": "751e5e2717a84b6e95e1d013224de5bf",
+ "m_Id": 3,
+ "m_DisplayName": "Exposure Weight",
+ "m_SlotType": 0,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "Exposure Weight",
+ "m_StageCapability": 3,
+ "m_Value": 0.800000011920929,
+ "m_DefaultValue": 1.0,
+ "m_Labels": []
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.Vector2MaterialSlot",
@@ -1206,6 +1429,36 @@
"inspectorFoldoutMask": 0
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.ColorRGBMaterialSlot",
+ "m_ObjectId": "80c5b8e752414a589e157a082c1876f0",
+ "m_Id": 1,
+ "m_DisplayName": "Color",
+ "m_SlotType": 0,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "Color",
+ "m_StageCapability": 3,
+ "m_Value": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "m_DefaultValue": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "m_Labels": [],
+ "m_ColorMode": 0,
+ "m_DefaultColor": {
+ "r": 0.0,
+ "g": 0.0,
+ "b": 0.0,
+ "a": 1.0
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.TangentMaterialSlot",
@@ -1230,6 +1483,30 @@
"m_Space": 0
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot",
+ "m_ObjectId": "81ac6aeaac01402b8b87d7d8b5117195",
+ "m_Id": 1,
+ "m_DisplayName": "B",
+ "m_SlotType": 0,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "B",
+ "m_StageCapability": 3,
+ "m_Value": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "w": 0.0
+ },
+ "m_DefaultValue": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "w": 0.0
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.Vector2MaterialSlot",
@@ -1318,9 +1595,9 @@
"m_Expanded": true,
"m_Position": {
"serializedVersion": "2",
- "x": -444.0,
- "y": -518.0,
- "width": 214.66664123535157,
+ "x": -511.33343505859377,
+ "y": -528.0000610351563,
+ "width": 208.0,
"height": 374.0
}
},
@@ -1472,6 +1749,36 @@
"m_OutputChannel": 0
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.ColorRGBMaterialSlot",
+ "m_ObjectId": "a718235381784603bb6c139dea579167",
+ "m_Id": 0,
+ "m_DisplayName": "Output",
+ "m_SlotType": 1,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "Output",
+ "m_StageCapability": 3,
+ "m_Value": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "m_DefaultValue": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "m_Labels": [],
+ "m_ColorMode": 1,
+ "m_DefaultColor": {
+ "r": 0.0,
+ "g": 0.0,
+ "b": 0.0,
+ "a": 1.0
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.PropertyNode",
@@ -1700,6 +2007,44 @@
"m_ObjectId": "bdc3a45891fa4a00b64f72f0271fe623"
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.AddNode",
+ "m_ObjectId": "c0ef6eda9aa241c089a8c97ea98ccd35",
+ "m_Group": {
+ "m_Id": ""
+ },
+ "m_Name": "Add",
+ "m_DrawState": {
+ "m_Expanded": true,
+ "m_Position": {
+ "serializedVersion": "2",
+ "x": -188.00006103515626,
+ "y": -608.6668090820313,
+ "width": 208.0,
+ "height": 302.0
+ }
+ },
+ "m_Slots": [
+ {
+ "m_Id": "1f7989ec260f4a26931027a955db33f0"
+ },
+ {
+ "m_Id": "81ac6aeaac01402b8b87d7d8b5117195"
+ },
+ {
+ "m_Id": "db45a4406ef4487c9e3bec32ba8ebbf1"
+ }
+ ],
+ "synonyms": [],
+ "m_Precision": 0,
+ "m_PreviewExpanded": true,
+ "m_PreviewMode": 0,
+ "m_CustomColors": {
+ "m_SerializableColors": []
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.PropertyNode",
@@ -1876,6 +2221,30 @@
"m_SerializedDescriptor": "VertexDescription.Position"
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot",
+ "m_ObjectId": "db45a4406ef4487c9e3bec32ba8ebbf1",
+ "m_Id": 2,
+ "m_DisplayName": "Out",
+ "m_SlotType": 1,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "Out",
+ "m_StageCapability": 3,
+ "m_Value": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "w": 0.0
+ },
+ "m_DefaultValue": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "w": 0.0
+ }
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.Internal.Texture2DShaderProperty",
@@ -1946,6 +2315,21 @@
"m_Labels": []
}
+{
+ "m_SGVersion": 0,
+ "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot",
+ "m_ObjectId": "f15c0863146c4d569fb25b836a2cacc2",
+ "m_Id": 0,
+ "m_DisplayName": "Emission",
+ "m_SlotType": 1,
+ "m_Hidden": false,
+ "m_ShaderOutputName": "Out",
+ "m_StageCapability": 3,
+ "m_Value": 0.0,
+ "m_DefaultValue": 0.0,
+ "m_Labels": []
+}
+
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.BlockNode",
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/advanced/camera-settings.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/advanced/camera-settings.md
index 9d2dd7a5f..52bc9bf35 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/advanced/camera-settings.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/advanced/camera-settings.md
@@ -22,11 +22,11 @@ To use the camera controller, select the `Camera` scene object, which is at the
- **Distance**: Moves the camera closer to or further away from the pivot point. This is used in conjunction with the FOV to frame the table in view.
- **FOV**: Sets the field of view of the camera. High values result in a wide field of view and more depth perspective. Low values narrow and flatten the view.
> [!note]
- > Adjusting the distance and FOV together will control how much apparent depth there is to tge table. A wide field of view and low distance will give the table a lit of perspective and depth, but will be more challenging to fit in the view without clipping the front, or having a lot of space to the sides in the back. Conversely, moving the camera further away and lowering the FOV will remove much of the perspective allowing the table to fit more evenly in the frame.
+ > Adjusting the distance and FOV together will control how much apparent depth there is to the table. A wide field of view and low distance will give the table a bit of perspective and depth, but will be more challenging to fit in the view without clipping the front, or having a lot of space to the sides in the back. Conversely, moving the camera further away and lowering the FOV will remove much of the perspective allowing the table to fit more evenly in the frame.
- **Orbit**: Controls the camera angle in an orbit around the table. 0 and 360 are standing in front of the table, 90 is on the left, 270 to the right.
## Presets
Camera presets are stored camera settings that allow you to rapidly switch between different camera views. This can be useful when developing a table to view specific areas or just to take pretty screenshots.
-The preset slider changes between the stored camera views. There are three views included by default. Any preset can be overwritten by changing the parameters and clicking the *Save* button. If *Save* is not pressed, the values will be lost when you cycle to a new preset. New presets can be made by adjusting the values and clicking *Clone*. This preset will be added to the presets at the end. You can delete any preset by setting the slider to that preset and clicking the *Delete* button.
\ No newline at end of file
+The preset slider changes between the stored camera views. There are three views included by default. Any preset can be overwritten by changing the parameters and clicking the *Save* button. If *Save* is not pressed, the values will be lost when you cycle to a new preset. New presets can be made by adjusting the values and clicking *Clone*. This preset will be added to the presets at the end. You can delete any preset by setting the slider to that preset and clicking the *Delete* button.
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager-lamp.png b/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager-lamp.png
index 6b875cb46..f590b6687 100644
Binary files a/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager-lamp.png and b/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager-lamp.png differ
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager.md
index 86e05ce98..4dbe2d771 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/coil-manager.md
@@ -1,9 +1,11 @@
---
+uid: coil_manager
+title: Coil Manager
description: VPE's coil manager lets you hook up the coils of the playfield to the gamelogic engine.
---
# Coil Manager
-On a real pinball table most moving parts, including the flippers, are triggered by [coils](https://en.wikipedia.org/wiki/Inductor) (also called [solenoids](https://en.wikipedia.org/wiki/Solenoid)). In VPE it's the job of the [gamelogic engine](~/creators-guide/manual/gamelogic-engine.md) to trigger them when needed.
+On a real pinball table most moving parts, including the flippers, are triggered by [coils](https://en.wikipedia.org/wiki/Inductor) (also called [solenoids](https://en.wikipedia.org/wiki/Solenoid)). In VPE it's the job of the [gamelogic engine](xref:gamelogic_engine) to trigger them when needed.
Just as the coils are physically wired to the power driver board on a regular machine they can be virtually connected in VPE using the coil manager under *Visual Pinball -> Coil Manager*.
@@ -29,8 +31,8 @@ The **Description** column is optional. If you're setting up a re-creation, you
The **Destination** column defines where the element in the following column is located. There are three options:
- *Playfield* lets you select a game element on the playfield that features the coil
-- *Device* lets you choose a *coil device*, a mechanism which may include multiple coils, such as a [trough](../manual/mechanisms/troughs.md).
-- *Lamp* sets the coil to be configured in the lamp manager (see [flashers in the lamp manager](lamp-manager.md#flashers) for more details).
+- *Device* lets you choose a *coil device*, a mechanism which may include multiple coils, such as a [trough](xref:troughs).
+- *Lamp* sets the coil to be configured in the lamp manager (see [flashers in the lamp manager](xref:lamp_manager#flashers) for more details).
### Element
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-coil.png b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-coil.png
index c3b798344..0b784b0ec 100644
Binary files a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-coil.png and b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-coil.png differ
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-gameplay.gif b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-gameplay.gif
index be280938e..51c8de3c4 100644
Binary files a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-gameplay.gif and b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager-gameplay.gif differ
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.md
index a7a5217e4..2c38c3d7e 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.md
@@ -1,4 +1,6 @@
---
+uid: lamp_manager
+title: Lamp Manager
description: The lamp manager lets you configure the lights, flashers, and general illumination of the playfield and connect them to the gamelogic engine.
---
# Lamp Manager
@@ -43,13 +45,9 @@ The first column, **ID** shows the name that the gamelogic engine exports for ea
The **Description** column is optional. If you're setting up a recreation, you would typically use this for the lamp name from the manual. It's purely for your own benefit, and you can leave this empty if you want.
-### Destination
+### Source
-The **Destination** column defines where the lamp is located. Currently, *Playfield* is the only option.
-
-### Element
-
-Under the **Element** column, you choose which lamp among the game items on the playfield should be controlled.
+GI lights can be emitted as a different type of light from the gamelogic engine. Set the **source** here in order to prevent conflicts of matching IDs between normal lights and GI lights.
### Type
@@ -60,9 +58,17 @@ The **Type** column defines how the signal is interpreted by the lamp. This is i
- *RGB Multi* - An RGB lamp that can change its color during gameplay. Lamps of this type receive three connections, one from each red, green and blue. Each color channel receives values as a fading lamp.
- *RGB* - An RGB lamp that receives its data from a single connection. This is the only mode where the lamp doesn't receive an integer, but an entire color value.
-### R G B
+### Element
-If the type of the previous column has been set to *RGB Multi*, here is where you link each wire to a color. Note that the *red* channel is always the one shown under the *ID* column, so changing the red link will also change the ID (and vice versa).
+Under the **Element** column, you choose which lamp among the game items on the playfield should be controlled.
+
+### Channel
+
+If the type of the previous column has been set to *RGB Multi*, here is where you define which **color channel** this output corresponds to.
+
+### Max. Intensity
+
+For fading lights, VPE expects values between `0` and `255`. However, GI light have usually less variations, typically eight. This column is only enabled for GI lights, and the value is the highest value for a given GI strand that the gamelogic engine will emit.
## Flashers
@@ -80,9 +86,7 @@ Note that you cannot change the *ID* of the lamp, because it's still linked to t
## GI Strands
-There is currently no special support for GI strands. In Visual Pinball, you can put GI lamps into a collection and address the whole collection at once via script. VPE doesn't have this feature yet. In order to hook up GI lamps, you can add an entry for each lamp and link all of them to the same ID.
-
-We'd like to make this easier in the future, so we're thinking of integrating this into the editor directly.
+In Visual Pinball, you can put GI lamps into a collection and address the whole collection at once via script. VPE provides [light groups](xref:light_groups) as a similar feature.
## Editor vs Runtime
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.png b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.png
index 84d106f39..e92b2aecb 100644
Binary files a/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.png and b/VisualPinball.Unity/Documentation~/creators-guide/editor/lamp-manager.png differ
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/materials.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/materials.md
index ae5577988..a0f1ba8d9 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/materials.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/materials.md
@@ -1,8 +1,11 @@
---
+uid: materials_index
title: Materials
description: How VPE deals with materials.
---
+
+
# Materials
Materials are what you apply to an object in order to make it look or behave like something in the real world. Materials are one of the key components of a table, because they define the visuals and the physical behavior. However, the term *material* can be confusing, because it can have different meanings. So let's define them first.
@@ -39,6 +42,4 @@ As mentioned above, there are two differences between Visual Pinball and VPE how
When importing a `.vpx` file, VPE converts the "visual part" of Visual Pinball materials into materials for the current render pipeline. It does that by creating a new material for every material/texture combination in Visual Pinball. The materials are then written to the `Materials` asset folder of the imported table where they can be easily edited and referenced. Since Visual Pinball uses different shaders than Unity, the results of the conversion are approximations and should be heavily tweaked.
-Since VPE uses the same physics engine as Visual Pinball, the physical values of the materials don't need to be converted, they are copied 1:1 into a new physics material and saved in the asset folder.
-
-In case you're using the Unity editor for authoring a Visual Pinball table, you can still edit the original VPX materials using the materials manager. However, you'll notice that the physical attributes are missing, since they are now handled by physical material assets. That means when exporting, VPE will apply the physics values from the assets to the internal materials (the match between the material editor and the asset is done via name).
\ No newline at end of file
+Since VPE uses the same physics engine as Visual Pinball, the physical values of the materials don't need to be converted, they are copied 1:1 into a new physics material and saved in the asset folder.
\ No newline at end of file
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/switch-manager.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/switch-manager.md
index 04e3e6fd3..3a31ddc94 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/switch-manager.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/switch-manager.md
@@ -1,9 +1,11 @@
---
+uid: switch_manager
+title: Switch Manager
description: VPE's switch manager lets you hook up the switches of the playfield to the gamelogic engine.
---
# Switch Manager
-During gameplay, the [gamelogic engine](~/creators-guide/manual/gamelogic-engine.md) needs to know what is happening on the playfield. For that, real pinball tables have switches on the playfield that signal when a ball rolls over or settles in a certain position. These switches are also built into targets, bumpers, kickers, and other mechanisms (see *[Supported Game Mechanisms](#supported-game-mechanisms)* below).
+During gameplay, the [gamelogic engine](xref:gamelogic_engine) needs to know what is happening on the playfield. For that, real pinball tables have switches on the playfield that signal when a ball rolls over or settles in a certain position. These switches are also built into targets, bumpers, kickers, and other mechanisms (see *[Supported Game Mechanisms](#supported-game-mechanisms)* below).
Wiring these switches up to the gamelogic engine with code can be a tedious process, so VPE provides a graphical interface where you can do it easily. If you've named them appropriately it can even guess which switch maps to which game item.
@@ -53,7 +55,7 @@ The **Source** column defines where the element in the following column originat
- *Playfield* lets you choose a game item from the playfield
- *Input System* lets you choose an input action from a pre-defined list, e.g. cabinet switches
- *Constant* sets the switch once at the beginning of the game to the given value.
-- *Device* lets you choose a switch device containing the switch. Switch devices are mechanisms that include multiple switches, for example [troughs](../manual/mechanisms/troughs.md).
+- *Device* lets you choose a switch device containing the switch. Switch devices are mechanisms that include multiple switches, for example [troughs](xref:troughs).
### Element
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/unity-components.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/unity-components.md
index d2d24d8a0..6324c6a96 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/unity-components.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/unity-components.md
@@ -1,14 +1,13 @@
---
+uid: unity_components
+title: Unity Components
description: Working with mesh, collider and animation components.
---
# Unity Components
When loading or creating a table in Unity, what you're creating is a hierarchy of [GameObjects](https://docs.unity3d.com/Manual/GameObjects.html), the basic building blocks of Unity scenes. By default we group game items by their type when importing, but you can arrange them however you want.
-To give the GameObjects behavior during gameplay, we add [components](https://docs.unity3d.com/Manual/Components.html) onto them. VPE comes with large number of components that are used to set up and control the game mechanisms of the table, and advanced programmers can contibute new ones.
-
-> [!note]
-> During runtime, VPE converts the GameObjects and components into entities in Unity's [DOTS workflow](https://unity.com/dots). There are special components used to control this conversion which we call *Authoring Components*.
+To give the GameObjects behavior during gameplay, we add [components](https://docs.unity3d.com/Manual/Components.html) onto them. VPE comes with a large number of components that are used to set up and control the game mechanisms of the table, and advanced programmers can contibute new ones.
If you've never heard about GameObjects or components, we recommmend you read through the links in the first two paragraphs. They are short, to the point, and a much better introduction than we could provide here.
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.md
index b77e5c893..2f258b3fb 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.md
@@ -1,11 +1,13 @@
---
+uid: wire_manager
+title: Wire Manager
description: VPE's wire manager lets you directly hook up any switch to any coil or lamp.
---
# Wire Manager
-Using the [Switch Manager](switch-manager.md), you can wire playfield and cabinet switches to the [Gamelogic Engine](../manual/gamelogic-engine.md). Similarly, the [Coil Manager](coil-manager.md) and [Lamp Manager](#) let you connect playfield elements to the outputs of the Gamelogic Engine.
+Using the [Switch Manager](xref:switch_manager), you can wire playfield and cabinet switches to the [Gamelogic Engine](xref:gamelogic_engine). Similarly, the [Coil Manager](xref:coil_manager) and [Lamp Manager](xref:lamp_manager) let you connect playfield elements to the outputs of the Gamelogic Engine.
-The **Wire Manager** allows you to *bypass* the gamelogic engine and connect switches directly to coils and lamps. This can be useful for debugging, but also for game logic that might not be covered by the gamelogic engine.
+The **Wire Manager** allows you to *bypass* the gamelogic engine and connect switches directly to coils and lamps. Using the *dynamic* wires, this can be used to eliminate the flipper lag often introduced by emulated ROMs. But it also can be useful for debugging, or for game logic that might not be covered by the gamelogic engine.
You can open the wire manager under *Visual Pinball -> Wire Manager*.
@@ -26,7 +28,7 @@ The **Source** column defines the type of source you are connecting to. There ar
- *Playfield* lets you select any game item that qualifies as a source from the playfield.
- *Input System* lets you select an input action from a pre-defined list, e.g. cabinet switches.
- *Constant* sets the destination to a constant value.
-- *Device* lets you select a source device. Such devices are mechanisms that include multiple sources, for example [troughs](../manual/mechanisms/troughs.md).
+- *Device* lets you select a source device. Such devices are mechanisms that include multiple sources, for example [troughs](xref:troughs).
### Source Element
@@ -45,12 +47,34 @@ Finally, if **Constant** is selected, you select the value that will be permanen
Under **Destination** you can select the type of the element that will *receive* the switch changes. There are two types to choose from:
- *Playfield* lets you select any game item that qualifies as a destination from the playfield
-- *Device* lets you choose a destination device. Such devices are mechanisms that include multiple coils or lamps, for example [troughs](../manual/mechanisms/troughs.md).
+- *Device* lets you choose a destination device. Such devices are mechanisms that include multiple coils or lamps, for example [troughs](xref:troughs).
### Destination Element
The **Destination Element** column is where you select which specific element in the destination column should receive switch changes. If *Device* was selected in the previous column, both the actual device and the element within the device have to be selected.
+### Dynamic
+
+By checking the **Dynamic** box, the wire dynamically enables and disables depending on the gamelogic engine's output. Used with flippers, this feature is also known as *Fast Flip* and can be used to reduce the lag introduced by an emulated gamelogic engine like PinMAME.
+
+If it's enabled and a game is started, VPE compares the switches and coils linked to the gamelogic engine and tries to find the switch and coil IDs that correspond to the wire. If found, VPE monitors the coil signals of the gamelogic engine. If the time between the switch and the coil response is below a threshold, the wire is enabled and future coil signals from the gamelogic engine are discarded. In the same way, if the wire is active and no coil signal is received within the threshold, the wire is disabled again.
+
+> [!note]
+> In order to match the switch and coil signals from the gamelogic engine, the switch and the coil need to be linked to the gamelogic engine using the *Switch Manager* and *Coil Manager* respectively. For example, it's not sufficient to just create a flipper button -> flipper coil wire and not link the flipper button to the flipper switch and the flipper coil to the flipper.
+
+There are a few edge cases that are handled as well. For example, if the wire is active and the gamelogic engine enables the coil without a switch signal (like the Phantom Flip in Monster Bash), the coil event is not discarded (VPE internally keeps a queue of switch events and if the queue is empty, coil events are still processed).
+
+However, by design, there are two caveats:
+
+1. When the wire is inactive, the first event always has the gamelogic engine lag, since VPE will only activate the wire when it's sure a coil event is emitted.
+2. When the gamelogic engine stops emitting coil events, VPE continues emitting the coil event one more time until it discovers the absence of the coil event and thus sends the negative signal after the threshold to "undo" its mistake.
+
+
+> [!note]
+> [MPF](xref:mpf_index) has a similar feature called [Hardware Rules](https://docs.missionpinball.org/en/dev/hardware/hw_rules.html#the-solution-hardware-rules). This is the preferred way, because the gamelogic engine explicitly notifies VPE about which wires to add and remove during gameplay.
+>
+> However, other gamelogic engines like PinMAME don't have this feature, that's why VPE comes with the *dynamic wire* feature that guesses when wire is active and when not.
+
### Pulse Delay
Internally, VPE connects switches to events. Some switchable game items only emit the *switch closed* event. Such items are spinners and targets. These are elements where the re-opening of the switch does not have any semantic value.
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.png b/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.png
index e4bdb438c..e93d26c11 100644
Binary files a/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.png and b/VisualPinball.Unity/Documentation~/creators-guide/editor/wire-manager.png differ
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md
index e706ef0b2..8f603e3ce 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md
@@ -1,4 +1,5 @@
---
+gui: displays
description: How VPE handles dot matrix and segment displays.
---
# Displays
@@ -8,7 +9,7 @@ Every pinball machine has one or more displays where the score and other importa

*A dot matrix display used in the late 90s - Photo © 2009 by [ElHeineken](https://commons.wikimedia.org/wiki/File:Pinball_Dot_Matrix_Display_-_Demolition_Man.JPG)*
-VPE supports both segment displays and dot matrix displays (usually referred to as DMDs). During game play displays are driven by the [Gamelogic Engine](gamelogic-engine.md). VPE supports multiple displays per game.
+VPE supports both segment displays and dot matrix displays (usually referred to as DMDs). During game play displays are driven by the [Gamelogic Engine](xref:gamelogic_engine). VPE supports multiple displays per game.
> [!note]
> While the earliest electro-mechanical pinball machines used motorized score reels, and today's are using high resolution LCDs neither are yet supported in VPE.
@@ -17,7 +18,7 @@ VPE supports both segment displays and dot matrix displays (usually referred to
Displays are lazily bound, meaning that when the game starts the gamelogic engine announces its displays and VPE connects them to objects in your scene that actually render them. Matching is done with an ID and depends on how the gamelogic engine being used manages displays.
-For example, in [MPF](../../plugins/mpf/index.md) you name your displays yourself in the machine configuration, while PinMAME uses IDs like `dmd0` and `display0` to identify its DMDs and segment displays.
+For example, in [MPF](xref:mpf_index) you name your displays yourself in the machine configuration, while PinMAME uses IDs like `dmd0` and `display0` to identify its DMDs and segment displays.
### Editor
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/gamelogic-engine.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/gamelogic-engine.md
index 802e4120b..3abcc6ccf 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/manual/gamelogic-engine.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/gamelogic-engine.md
@@ -1,4 +1,6 @@
---
+uid: gamelogic_engine
+title: Gamelogic Engine
description: VPE's gamelogic engine is the code driving your game's logic.
---
# Gamelogic Engine
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-group-inspector.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-group-inspector.png
new file mode 100644
index 000000000..20a61c2ba
Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-group-inspector.png differ
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md
new file mode 100644
index 000000000..c8d57803d
--- /dev/null
+++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md
@@ -0,0 +1,37 @@
+---
+uid: light_groups
+title: Light Groups
+description: VPE can group and address multiple lights at once.
+---
+
+# Light Groups
+
+Sometimes, a game addresses multiple physical lights as one logical lamp, i.e. all lights are always toggled or faded at the same time. Typical use cases are [GI strips](https://docs.missionpinball.org/en/latest/mechs/lights/gis.html). Instead of creating a link in the [Lamp Manager](xref:lamp_manager) for each light separately, VPE ships with a component called *Lamp Group*.
+
+A light group is a component you can add to any GameObject. It's recommended to make it parent of the light objects it contains, but you can also keep it outside of the lights hierarchy, since it explicitly references the lights it contains.
+
+## Setup
+
+
+
+To create a new light group, select the GameObject you want to add your light group to, and in the inspector click on *Add Component* and choose *Visual Pinball -> Game Item -> Light Group*.
+
+Then use the list control to add and remove lights. There are a few button that make this easier.
+
+### Add Children
+
+In case you have parented the light groups component to the light GameObjects it should include, this button adds all child lights to the list. Existing lights will remain.
+
+### Replace With Children
+
+Similar to *Add Children*, only that the list is cleared before adding new lights.
+
+### Clear
+
+Simply clears the list.
+
+### Select Light Sources
+
+When working with lights, the GameObject with the actual light source is nested within the main object. This can make adjusting light settings for multiple lights tedious, since you have to drill into each parent in order to select the source.
+
+This button selects all the source GameObjects for the lights in the light group.
\ No newline at end of file
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/troughs.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/troughs.md
index ac78f9ea6..c2b285dba 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/troughs.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/troughs.md
@@ -1,8 +1,14 @@
+---
+uid: troughs
+title: Troughs / Ball Drains
+description: How VPE works with troughs (ball drains).
+---
+
# Troughs / Ball Drains
If you are unfamiliar with ball troughs, have a quick look at [MPF's documentation](https://mpf-docs.readthedocs.io/en/latest/mechs/troughs/), which does an excellent job explaining them.
-VPE comes with a trough mechanism that simulates the behavior of a real-world ball trough. This is especially important when emulating existing games, since the [gamelogic engine](../gamelogic-engine.md) expects the trough's switches to be in a plausible state, or else it may have errors.
+VPE comes with a trough mechanism that simulates the behavior of a real-world ball trough. This is especially important when emulating existing games, since the [gamelogic engine](xref:gamelogic_engine) expects the trough's switches to be in a plausible state, or else it may have errors.
## Creating a Trough
@@ -97,13 +103,13 @@ A single ball trough may work [with](https://docs.missionpinball.org/en/latest/m
The number of simulated switches in the trough depends on the type of trough and the *Switch Count* property in the inspector panel. For recreations, you can quickly determine the number of trough switches by looking at the switch matrix in the operation manual, it usually matches the number of balls installed in the game.
-To configure the switches, open the [switch manager](../../editor/switch-manager.md) and add the trough switches if they're not already there. For *Destination* select "Device", under *Element*, select the trough you've created and which switch to connect. For a modern five-ball trough, it will look something like this:
+To configure the switches, open the [switch manager](xref:switch_manager) and add the trough switches if they're not already there. For *Destination* select "Device", under *Element*, select the trough you've created and which switch to connect. For a modern five-ball trough, it will look something like this:

## Coil Setup
-VPE's trough supports up to two coils, an entry coil which drains the ball from the outhole into the trough, and an eject coil which pushes a new ball into the plunger lane. To configure the coils, open the [coil manager](../../editor/coil-manager.md), find or add the coils, and link them to the trough like you did with the switches:
+VPE's trough supports up to two coils, an entry coil which drains the ball from the outhole into the trough, and an eject coil which pushes a new ball into the plunger lane. To configure the coils, open the [coil manager](xref:coil_manager), find or add the coils, and link them to the trough like you did with the switches:

diff --git a/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md b/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md
index c8be45810..8d235b6bc 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md
@@ -76,4 +76,4 @@ Click *Yes*. When complete, you should now have a *Visual Pinball* menu in the e

-Now that VPE is installed let's [import a table](running-vpe.md)!
+Now that VPE is installed let's [import a table](xref:setup_running_vpe)!
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md b/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md
index 47a267ed2..48f929ce8 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md
@@ -1,4 +1,5 @@
---
+guid: setup_running_vpe
description: How to run VPE
---
# Running VPE
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml
index f63844590..f62cb5b9c 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml
+++ b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml
@@ -47,3 +47,5 @@
href: manual/mechanisms/flippers.md
- name: Slingshots
href: manual/mechanisms/slingshots.md
+ - name: Light Groups
+ href: manual/mechanisms/light-groups.md
diff --git a/VisualPinball.Unity/Documentation~/docfx.json b/VisualPinball.Unity/Documentation~/docfx.json
index fa2e8fe2e..1ea0fa126 100644
--- a/VisualPinball.Unity/Documentation~/docfx.json
+++ b/VisualPinball.Unity/Documentation~/docfx.json
@@ -4,7 +4,8 @@
"src": [
{
"files": [
- "VisualPinball.Unity/**.csproj"
+ "VisualPinball.Unity/**.csproj",
+ "VisualPinball.Unity.Patcher/**.csproj"
],
"src": "../"
}
diff --git a/VisualPinball.Unity/Documentation~/plugins/index.md b/VisualPinball.Unity/Documentation~/plugins/index.md
index 2b6e1f355..578d8ff1a 100644
--- a/VisualPinball.Unity/Documentation~/plugins/index.md
+++ b/VisualPinball.Unity/Documentation~/plugins/index.md
@@ -1,4 +1,5 @@
---
+uid: plugins_index
title: Plugins
description: Visual Pinball for Unity - Plugins
---
@@ -8,6 +9,6 @@ description: Visual Pinball for Unity - Plugins
VPE has a plug-in system that allows other software to integrate with it. Plugins are typically required on a per-table basis. VPE ships with a number of default plugins which are documented here.
-## [Mission Pinball Framework](mpf/index.html)
+## [Mission Pinball Framework](xref:mpf_index)
The [Mission Pinball Framework](https://missionpinball.org/) is software written in Python that is used to drive real pinball machines. It integrates with VPE as a gamelogic engine.
diff --git a/VisualPinball.Unity/Documentation~/plugins/mpf/index.md b/VisualPinball.Unity/Documentation~/plugins/mpf/index.md
index c3d005e60..a93ce34a9 100644
--- a/VisualPinball.Unity/Documentation~/plugins/mpf/index.md
+++ b/VisualPinball.Unity/Documentation~/plugins/mpf/index.md
@@ -1,4 +1,5 @@
---
+uid: mpf_index
title: Mission Pinball Framework
description: Visual Pinball Engine integration with the Mission Pinball Framework.
---
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs
index 146fb2b48..b66afcc26 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs
@@ -92,7 +92,7 @@ public VpxSceneConverter(FileTableContainer sourceContainer, string fileName = "
_sourceContainer = sourceContainer;
_sourceTable = sourceContainer.Table;
_patcher = PatcherManager.GetPatcher();
- _patcher?.Set(sourceContainer, fileName);
+ _patcher?.Set(sourceContainer, fileName, this, this);
_options = options ?? new ConvertOptions();
}
@@ -155,6 +155,11 @@ public GameObject Convert(bool applyPatch = true, string tableName = null)
ConfigurePlayer(componentLookup);
+ // patch
+ if (_applyPatch) {
+ _patcher?.PostPatch(_tableGo);
+ }
+
return _tableGo;
}
@@ -261,11 +266,15 @@ private Dictionary UpdateGameItems(Dictionary components)
_tableGo.AddComponent();
var dga = _tableGo.AddComponent();
- // add trough if none available
- if (!_sourceContainer.HasTrough) {
- CreateTrough(components);
- }
-
// populate hardware
_tableComponent.RepopulateHardware(dga);
}
- private void CreateTrough(Dictionary components)
- {
- var troughData = new TroughData("Trough") {
- BallCount = 4,
- SwitchCount = 4,
- Type = TroughType.ModernMech
- };
- if (_sourceContainer.Has("BallRelease")) {
- troughData.PlayfieldExitKicker = "BallRelease";
- }
- if (_sourceContainer.Has("Drain")) {
- troughData.PlayfieldEntrySwitch = "Drain";
- }
- var item = new Trough(troughData) {
- StorageIndex = _sourceContainer.ItemDatas.Count()
- };
-
- InstantiateAndPersistPrefab(item, components);
- }
-
private void CreateFileHierarchy()
{
if (!Directory.Exists("Assets/Tables/")) {
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs
index 85fccf124..ef6fb3c5e 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs
@@ -68,6 +68,14 @@ public override void OnInspectorGUI()
mb.Roundness = roundness;
}
}
+
+ var emission = EditorGUILayout.Slider("Emission (nits)", _mb.Emission, 1f, 500f);
+ if (emission != _mb.Emission) {
+ RecordUndo("Change DMD Dot Emission", this);
+ foreach (var mb in _mbs) {
+ mb.Emission = emission;
+ }
+ }
}
[MenuItem("GameObject/Visual Pinball/Dot Matrix Display", false, 12)]
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilListViewItemRenderer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilListViewItemRenderer.cs
index 2cc684f24..30618a602 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilListViewItemRenderer.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilListViewItemRenderer.cs
@@ -76,7 +76,7 @@ public void Render(TableComponent tableComponent, CoilListData data, Rect cellRe
private void UpdateId(CoilListData data, string id)
{
if (data.Destination == CoilDestination.Lamp) {
- var lampEntry = _tableComponent.MappingConfig.Lamps.FirstOrDefault(l => l.Id == data.Id && l.Source == LampSource.Coils);
+ var lampEntry = _tableComponent.MappingConfig.Lamps.FirstOrDefault(l => l.Id == data.Id && l.IsCoil);
if (lampEntry != null) {
lampEntry.Id = id;
LampManager.Refresh();
@@ -95,7 +95,7 @@ private void RenderDestination(CoilListData coilListData, Rect cellRect, Action<
{
if (coilListData.Destination == CoilDestination.Lamp) {
- var lampEntry = _tableComponent.MappingConfig.Lamps.FirstOrDefault(l => l.Id == coilListData.Id && l.Source == LampSource.Coils);
+ var lampEntry = _tableComponent.MappingConfig.Lamps.FirstOrDefault(l => l.Id == coilListData.Id && l.IsCoil);
if (lampEntry != null) {
_tableComponent.MappingConfig.RemoveLamp(lampEntry);
LampManager.Refresh();
@@ -104,7 +104,8 @@ private void RenderDestination(CoilListData coilListData, Rect cellRect, Action<
} else if (index == CoilDestination.Lamp) {
_tableComponent.MappingConfig.AddLamp(new LampMapping {
Id = coilListData.Id,
- Source = LampSource.Coils,
+ Source = LampSource.Lamp,
+ IsCoil = true,
Description = coilListData.Description
});
LampManager.Refresh();
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs
index 6f08d7bf2..61685980f 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs
@@ -138,7 +138,7 @@ protected override void RemoveData(string undoName, CoilListData data)
// todo if it's a lamp, also delete the lamp entry.
if (data.CoilMapping.Destination == CoilDestination.Lamp) {
- var lampEntry = TableComponent.MappingConfig.Lamps.FirstOrDefault(l => l.Id == data.Id && l.Source == LampSource.Coils);
+ var lampEntry = TableComponent.MappingConfig.Lamps.FirstOrDefault(l => l.Id == data.Id && l.IsCoil);
if (lampEntry != null) {
TableComponent.MappingConfig.RemoveLamp(lampEntry);
LampManager.Refresh();
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListData.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListData.cs
index 94e3d596f..36196630b 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListData.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListData.cs
@@ -15,6 +15,7 @@
// along with this program. If not, see .
using VisualPinball.Engine.Game.Engines;
+using VisualPinball.Engine.Math;
namespace VisualPinball.Unity.Editor
{
@@ -25,39 +26,44 @@ public class LampListData : IManagerListData, IDeviceListData DeviceComponent => Device;
public LampListData(LampMapping lampMapping)
{
Id = lampMapping.Id;
+ InternalId = lampMapping.InternalId;
+ IsCoil = lampMapping.IsCoil;
Source = lampMapping.Source;
Description = lampMapping.Description;
Device = lampMapping.Device;
DeviceItem = lampMapping.DeviceItem;
Type = lampMapping.Type;
- Green = lampMapping.Green;
- Blue = lampMapping.Blue;
+ Channel = lampMapping.Channel;
+ FadingSteps = lampMapping.FadingSteps;
LampMapping = lampMapping;
}
@@ -65,13 +71,15 @@ public LampListData(LampMapping lampMapping)
public void Update()
{
LampMapping.Id = Id;
+ LampMapping.InternalId = InternalId;
+ LampMapping.IsCoil = IsCoil;
LampMapping.Source = Source;
LampMapping.Description = Description;
LampMapping.Device = Device;
LampMapping.DeviceItem = DeviceItem;
LampMapping.Type = Type;
- LampMapping.Green = Green;
- LampMapping.Blue = Blue;
+ LampMapping.Channel = Channel;
+ LampMapping.FadingSteps = FadingSteps;
}
}
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs
index 9f17aee45..0cd142f56 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs
@@ -19,6 +19,8 @@
using UnityEditor;
using UnityEngine;
using VisualPinball.Engine.Game.Engines;
+using VisualPinball.Engine.Math;
+using Color = UnityEngine.Color;
namespace VisualPinball.Unity.Editor
{
@@ -32,9 +34,11 @@ private enum LampListColumn
{
Id = 0,
Description = 1,
- Element = 2,
+ Source = 2,
Type = 3,
- Color = 4,
+ Element = 4,
+ Channel = 5,
+ FadingSteps = 6,
}
private readonly List _gleLamps;
@@ -56,7 +60,7 @@ public void Render(TableComponent tableComponent, LampListData data, Rect cellRe
switch ((LampListColumn)column) {
case LampListColumn.Id:
- if (data.Source == LampSource.Coils) {
+ if (data.IsCoil) {
RenderCoilId(lampStatuses, data, cellRect);
} else {
RenderId(lampStatuses, ref data.Id, id => data.Id = id, data, cellRect, updateAction);
@@ -65,19 +69,22 @@ public void Render(TableComponent tableComponent, LampListData data, Rect cellRe
case LampListColumn.Description:
RenderDescription(data, cellRect, updateAction);
break;
- case LampListColumn.Element:
- RenderDevice(data, cellRect, updateAction);
+ case LampListColumn.Source:
+ RenderSource(data, cellRect, updateAction);
break;
case LampListColumn.Type:
RenderType(data, cellRect, updateAction);
break;
- case LampListColumn.Color:
- switch (data.Type) {
- case LampType.RgbMulti:
- RenderRgb(lampStatuses, data, cellRect, updateAction);
- break;
- }
+ case LampListColumn.Element:
+ RenderDevice(data, cellRect, updateAction);
+ break;
+ case LampListColumn.Channel:
+ RenderChannel(data, cellRect, updateAction);
break;
+ case LampListColumn.FadingSteps:
+ RenderFadingSteps(data, cellRect, updateAction);
+ break;
+
}
EditorGUI.EndDisabledGroup();
}
@@ -85,10 +92,9 @@ public void Render(TableComponent tableComponent, LampListData data, Rect cellRe
private void RenderCoilId(Dictionary lampStatuses, LampListData lampListData, Rect cellRect)
{
// add some padding
- cellRect.x += 2;
+ cellRect.x = cellRect.width - 45;
cellRect.width -= 4;
-
var statusAvail = Application.isPlaying && lampStatuses != null && lampStatuses.ContainsKey(lampListData.Id);
var icon = Icons.Coil(IconSize.Small, statusAvail && lampStatuses[lampListData.Id] > 0 ? IconColor.Orange : IconColor.Gray);
if (icon != null) {
@@ -123,17 +129,40 @@ private void RenderType(LampListData lampListData, Rect cellRect, Action lampStatuses, LampListData data, Rect cellRect, Action updateAction)
+ private void RenderSource(LampListData lampListData, Rect cellRect, Action updateAction)
{
- var pad = 2;
- var width = cellRect.width / 3;
- var c = cellRect;
- c.width = width - pad;
- RenderId(lampStatuses, ref data.Id, id => data.Id = id, data, c, updateAction);
- c.x += width + pad;
- RenderId(lampStatuses, ref data.Green, id => data.Green = id, data, c, updateAction);
- c.x += width + pad;
- RenderId(lampStatuses, ref data.Blue, id => data.Blue = id, data, c, updateAction);
+ EditorGUI.BeginChangeCheck();
+ var source = (LampSource)EditorGUI.EnumPopup(cellRect, lampListData.Source);
+ if (EditorGUI.EndChangeCheck()) {
+ lampListData.Source = source;
+ updateAction(lampListData);
+ }
+ }
+
+ private void RenderChannel(LampListData lampListData, Rect cellRect, Action updateAction)
+ {
+ if (lampListData.Type != LampType.RgbMulti) {
+ return;
+ }
+ EditorGUI.BeginChangeCheck();
+ var channel = (ColorChannel)EditorGUI.EnumPopup(cellRect, lampListData.Channel);
+ if (EditorGUI.EndChangeCheck()) {
+ lampListData.Channel = channel;
+ updateAction(lampListData);
+ }
+ }
+
+ private void RenderFadingSteps(LampListData lampListData, Rect cellRect, Action updateAction)
+ {
+ if (lampListData.Type != LampType.SingleFading) {
+ return;
+ }
+ EditorGUI.BeginChangeCheck();
+ var value = EditorGUI.IntField(cellRect, lampListData.FadingSteps);
+ if (EditorGUI.EndChangeCheck()) {
+ lampListData.FadingSteps = value;
+ updateAction(lampListData);
+ }
}
protected override Texture GetIcon(LampListData lampListData)
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs
index 59342eb04..6faf8304c 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs
@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NLog;
using UnityEditor;
using UnityEngine;
@@ -42,6 +43,8 @@ internal class LampManager : ManagerWindow
private LampListViewItemRenderer _listViewItemRenderer;
+ private ToggleAction _toggleAction = ToggleAction.All;
+
public static void Refresh()
{
if (HasOpenInstances()) {
@@ -106,6 +109,49 @@ protected override void OnButtonBarGUI()
Reload();
}
}
+
+ GUILayout.FlexibleSpace();
+
+ _toggleAction = (ToggleAction)EditorGUILayout.EnumPopup(_toggleAction);
+ if (GUILayout.Button("Turn On", GUILayout.ExpandWidth(false))) {
+ Toggle(l => l.enabled = true);
+ }
+ if (GUILayout.Button("Turn Off", GUILayout.ExpandWidth(false))) {
+ Toggle(l => l.enabled = false);
+ }
+ if (GUILayout.Button("Select", GUILayout.ExpandWidth(false))) {
+ var lights = new List();
+ Toggle(lights.Add);
+ Selection.objects = lights.Select(l => l.gameObject as Object).ToArray();
+ }
+ }
+
+ private void Toggle(Action action)
+ {
+ if (TableComponent != null) {
+ IEnumerable selection = _toggleAction switch {
+ ToggleAction.All => TableComponent.MappingConfig.Lamps,
+ ToggleAction.Inserts => TableComponent.MappingConfig.Lamps.Where(lm => !lm.IsCoil && lm.Source == LampSource.Lamp),
+ ToggleAction.GI => TableComponent.MappingConfig.Lamps.Where(lm => lm.Source == LampSource.GI),
+ ToggleAction.Flasher => TableComponent.MappingConfig.Lamps.Where(lm => lm.IsCoil),
+ ToggleAction.Selected => _listView.GetSelectedData().Select(lld => lld.LampMapping),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ foreach (var lampMapping in selection) {
+ if (lampMapping.Device == null) {
+ continue;
+ }
+
+ var lights = lampMapping.Device is LightGroupComponent lightGroupComponent
+ ? lightGroupComponent.Lights.SelectMany(l => l.GetComponentsInChildren())
+ : lampMapping.Device.gameObject.GetComponentsInChildren();
+
+ foreach (var light in lights) {
+ action(light);
+ }
+ }
+ }
}
protected override void OnListViewItemRenderer(LampListData data, Rect cellRect, int column)
@@ -149,12 +195,13 @@ protected override void CloneData(string undoName, string newName, LampListData
TableComponent.MappingConfig.AddLamp(new LampMapping {
Id = data.Id,
+ InternalId = data.InternalId,
Description = data.Description,
Device = data.Device,
DeviceItem = data.DeviceItem,
Type = data.Type,
- Blue = data.Blue,
- Green = data.Green,
+ Source = data.Source,
+ IsCoil = data.IsCoil,
});
}
#endregion
@@ -195,4 +242,9 @@ private void RecordUndo(string undoName)
#endregion
}
+
+ internal enum ToggleAction
+ {
+ All, Inserts, GI, Flasher, Selected
+ }
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/ManagerWindow.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/ManagerWindow.cs
index 3d75b3602..b57b77958 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/ManagerWindow.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/ManagerWindow.cs
@@ -67,7 +67,7 @@ protected float RowHeight {
protected T _selectedItem;
private List _data = new List();
- private ManagerListView _listView;
+ protected ManagerListView _listView;
private TreeViewState _treeViewState;
private bool _renaming;
private string _renameBuffer = string.Empty;
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListData.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListData.cs
index 56687e29a..ca925036b 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListData.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListData.cs
@@ -20,6 +20,8 @@ namespace VisualPinball.Unity.Editor
{
public class WireListData : IManagerListData, IDeviceListData
{
+ public string Id;
+
[ManagerListColumn(Order = 0, HeaderName = "Description", Width = 150)]
public string Name { get; private set; }
@@ -27,40 +29,41 @@ public class WireListData : IManagerListData, IDeviceListData Name; set => Name = value; }
-
public string SourceInputActionMap;
public string SourceInputAction;
public SwitchConstant SourceConstant;
public ISwitchDeviceComponent SourceDevice;
public string SourceDeviceItem;
+ [ManagerListColumn(Order = 3, HeaderName = "Destination Element", Width = 270)]
public IWireableComponent DestinationDevice;
public string DestinationDeviceItem;
+ [ManagerListColumn(Order = 4, HeaderName = "Dynamic", Width = 100)]
+ public bool IsDynamic;
+
+ [ManagerListColumn(Order = 5, HeaderName = "Pulse Delay", Width = 100)]
+ public int PulseDelay;
+
+ public string Description { get => Name; set => Name = value; }
+
public readonly WireMapping WireMapping;
public IDeviceComponent DeviceComponent => DestinationDevice;
public string DeviceItem { get => DestinationDeviceItem; set => DestinationDeviceItem = value; }
public int InternalId { get; set; }
- public WireListData(WireMapping wireMapping) {
+ public WireListData(WireMapping wireMapping)
+ {
+ Id = wireMapping.Id;
Description = wireMapping.Description;
-
Source = wireMapping.Source;
SourceInputActionMap = wireMapping.SourceInputActionMap;
SourceInputAction = wireMapping.SourceInputAction;
SourceConstant = wireMapping.SourceConstant;
SourceDevice = wireMapping.SourceDevice;
SourceDeviceItem = wireMapping.SourceDeviceItem;
+ IsDynamic = wireMapping.IsDynamic;
DestinationDevice = wireMapping.DestinationDevice;
DestinationDeviceItem = wireMapping.DestinationDeviceItem;
@@ -80,6 +83,7 @@ public void Update()
WireMapping.SourceConstant = SourceConstant;
WireMapping.SourceDevice = SourceDevice;
WireMapping.SourceDeviceItem = SourceDeviceItem;
+ WireMapping.IsDynamic = IsDynamic;
WireMapping.DestinationDevice = DestinationDevice;
WireMapping.DestinationDeviceItem = DestinationDeviceItem;
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListViewItemRenderer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListViewItemRenderer.cs
index 5e2ff01d4..89ef4ecc7 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListViewItemRenderer.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Wire/WireListViewItemRenderer.cs
@@ -17,6 +17,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
using VisualPinball.Engine.Game.Engines;
@@ -27,7 +28,7 @@ public class WireListViewItemRenderer: ListViewItemRenderer GleItems => new List();
protected override IGamelogicEngineDeviceItem InstantiateGleItem(string id) => null;
- protected override Texture2D StatusIcon(bool status) => null;
+ protected override Texture2D StatusIcon(bool status) => Icons.Plug(IconSize.Small, status ? IconColor.Orange : IconColor.Gray);
private struct InputSystemEntry
{
@@ -41,7 +42,8 @@ private enum WireListColumn
Source = 1,
SourceElement = 2,
DestinationElement = 3,
- PulseDelay = 4,
+ Dynamic = 4,
+ PulseDelay = 5,
}
private readonly InputManager _inputManager;
@@ -59,10 +61,13 @@ public WireListViewItemRenderer(TableComponent tableComponent, InputManager inpu
public void Render(TableComponent tableComponent, WireListData data, Rect cellRect, int column, Action updateAction)
{
- switch ((WireListColumn)column)
- {
+ EditorGUI.BeginDisabledGroup(Application.isPlaying);
+ var switchStatuses = Application.isPlaying
+ ? tableComponent.gameObject.GetComponent()?.WireStatuses
+ : null;
+ switch ((WireListColumn)column) {
case WireListColumn.Description:
- RenderDescription(data, cellRect, updateAction);
+ RenderDescription(switchStatuses, data, cellRect, updateAction);
break;
case WireListColumn.Source:
RenderSource(data, cellRect, updateAction);
@@ -73,10 +78,33 @@ public void Render(TableComponent tableComponent, WireListData data, Rect cellRe
case WireListColumn.DestinationElement:
RenderDestinationElement(data, cellRect, updateAction);
break;
+ case WireListColumn.Dynamic:
+ RenderIsDynamic(switchStatuses, data, cellRect, updateAction);
+ break;
case WireListColumn.PulseDelay:
RenderPulseDelay(data, cellRect, updateAction);
break;
}
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void RenderDescription(Dictionary statuses, WireListData listData, Rect cellRect, Action updateAction)
+ {
+ if (Application.isPlaying && statuses != null) {
+ var iconRect = cellRect;
+ iconRect.width = 20;
+ if (statuses.ContainsKey(listData.Id)) {
+ var status = statuses[listData.Id];
+ var icon = StatusIcon(status.Item1);
+ var guiColor = GUI.color;
+ GUI.color = Color.clear;
+ EditorGUI.DrawTextureTransparent(iconRect, icon, ScaleMode.ScaleToFit);
+ GUI.color = guiColor;
+ }
+ cellRect.x += 25;
+ cellRect.width -= 25;
+ }
+ base.RenderDescription(listData, cellRect, updateAction);
}
private void RenderSource(WireListData wireListData, Rect cellRect, Action updateAction)
@@ -191,7 +219,7 @@ private void RenderSourceElementDevice(WireListData wireListData, Rect cellRect,
});
}
- private void RenderSourceElementDeviceItem(WireListData wireListData, Rect cellRect, Action updateAction)
+ private static void RenderSourceElementDeviceItem(WireListData wireListData, Rect cellRect, Action updateAction)
{
EditorGUI.BeginDisabledGroup(wireListData.SourceDevice == null);
@@ -231,12 +259,35 @@ protected override void RenderDeviceElement(WireListData listData, Rect cellRect
});
}
- private void RenderPulseDelay(WireListData wireListData, Rect cellRect, Action updateAction)
+
+ private static void RenderIsDynamic(IReadOnlyDictionary statuses, WireListData wireListData, Rect cellRect, Action updateAction)
+ {
+ if (Application.isPlaying && statuses != null) {
+ var status = statuses[wireListData.Id];
+ var lag = status.Item2;
+ var displayLag = math.round(lag * 10000f) / 10f;
+ if (lag < 0) {
+ EditorGUI.LabelField(cellRect, "Measuring...");
+ return;
+ }
+ if (lag > 0) {
+ EditorGUI.LabelField(cellRect, $"{displayLag} ms");
+ return;
+ }
+ }
+ EditorGUI.BeginChangeCheck();
+ var isDynamic = EditorGUI.Toggle(cellRect, wireListData.IsDynamic);
+ if (EditorGUI.EndChangeCheck()) {
+ wireListData.IsDynamic = isDynamic;
+ updateAction(wireListData);
+ }
+ }
+
+ private static void RenderPulseDelay(WireListData wireListData, Rect cellRect, Action updateAction)
{
if (wireListData.SourceDevice != null && !string.IsNullOrEmpty(wireListData.SourceDeviceItem)) {
var switchable = wireListData.SourceDevice.AvailableSwitches.First(s => s.Id == wireListData.SourceDeviceItem);
- if (switchable.IsPulseSwitch)
- {
+ if (switchable.IsPulseSwitch) {
var labelRect = cellRect;
labelRect.x += labelRect.width - 20;
labelRect.width = 20;
@@ -257,7 +308,7 @@ private void RenderPulseDelay(WireListData wireListData, Rect cellRect, Action.
+using System;
using System.Collections.Generic;
using NLog;
using UnityEditor;
using UnityEngine;
+using VisualPinball.Engine.Game.Engines;
using Logger = NLog.Logger;
+using Object = UnityEngine.Object;
namespace VisualPinball.Unity.Editor
{
@@ -91,6 +94,13 @@ protected override bool SetupCompleted()
protected override void OnButtonBarGUI()
{
+ if (GUILayout.Button("Populate All", GUILayout.ExpandWidth(false)))
+ {
+ RecordUndo("Populate all wire mappings");
+ TableComponent.MappingConfig.PopulateWires(GetAvailableEngineWires(), TableComponent);
+ Reload();
+ }
+
if (GUILayout.Button("Remove All", GUILayout.ExpandWidth(false)))
{
if (EditorUtility.DisplayDialog("Wire Manager", "Are you sure want to remove all wire mappings?", "Yes", "Cancel")) {
@@ -124,7 +134,7 @@ protected override void AddNewData(string undoName, string newName)
{
RecordUndo(undoName);
- TableComponent.MappingConfig.AddWire(new WireMapping());
+ TableComponent.MappingConfig.AddWire(new WireMapping().WithId());
}
protected override void RemoveData(string undoName, WireListData data)
@@ -138,12 +148,19 @@ protected override void CloneData(string undoName, string newName, WireListData
{
RecordUndo(undoName);
- TableComponent.MappingConfig.AddWire(new WireMapping());
+ TableComponent.MappingConfig.AddWire(new WireMapping().WithId());
}
#endregion
#region Helper methods
+
+ private GamelogicEngineWire[] GetAvailableEngineWires()
+ {
+ var gle = TableComponent.gameObject.GetComponent();
+ return gle == null ? Array.Empty() : gle.AvailableWires;
+ }
+
private void DisplayMessage(string message)
{
GUILayout.BeginHorizontal();
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs
index a23abd9e2..50d9fcb34 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs
@@ -40,11 +40,13 @@ public IconVariant(string name, IconSize size, IconColor color)
private const string BoltName = "bolt";
private const string CoilName = "coil";
private const string DropTargetName = "drop_target";
+ private const string FlasherName = "light_flasher";
private const string FlipperName = "flipper";
private const string GateName = "gate";
private const string HitTargetName = "hit_target";
private const string KeyName = "keyboard";
private const string KickerName = "kicker";
+ private const string LightGroupName = "light_group";
private const string LightName = "light";
private const string PlayfieldName = "playfield";
private const string PlungerName = "plunger";
@@ -62,9 +64,9 @@ public IconVariant(string name, IconSize size, IconColor color)
private const string SwitchNoName = "switch_no";
private static readonly string[] Names = {
- BumperName, BoltName, CoilName, DropTargetName, FlipperName, HitTargetName, GateName, KeyName, KickerName, LightName, PlayfieldName,
- PlungerName, PlugName, PrimitiveName, RampName, RubberName, SpinnerName, SurfaceName, TableName, TriggerName, TroughName,
- SlingshotName, SwitchNcName, SwitchNoName
+ BumperName, BoltName, CoilName, DropTargetName, FlasherName, FlipperName, HitTargetName, GateName, KeyName, KickerName, LightGroupName,
+ LightName, PlayfieldName, PlungerName, PlugName, PrimitiveName, RampName, RubberName, SpinnerName, SurfaceName, TableName, TriggerName,
+ TroughName, SlingshotName, SwitchNcName, SwitchNoName
};
private readonly Dictionary _icons = new Dictionary();
@@ -97,11 +99,13 @@ private Icons()
public static Texture2D Bumper(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(BumperName, size, color);
public static Texture2D DropTarget(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(DropTargetName, size, color);
+ public static Texture2D Flasher(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(FlasherName, size, color);
public static Texture2D Flipper(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(FlipperName, size, color);
public static Texture2D Gate(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(GateName, size, color);
public static Texture2D HitTarget(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(HitTargetName, size, color);
public static Texture2D Kicker(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KickerName, size, color);
public static Texture2D Light(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightName, size, color);
+ public static Texture2D LightGroup(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightGroupName, size, color);
public static Texture2D Playfield(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlayfieldName, size, color);
public static Texture2D Plunger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlungerName, size, color);
public static Texture2D Plug(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlugName, size, color);
@@ -125,11 +129,13 @@ public static Texture2D ByComponent(T mb, IconSize size = IconSize.Large, Ico
switch (mb) {
case BumperComponent _: return Bumper(size, color);
case DropTargetComponent _: return DropTarget(size, color);
+ //case FlasherComponent _: return Flasher(size, color);
case FlipperComponent _: return Flipper(size, color);
case GateComponent _: return Gate(size, color);
case HitTargetComponent _: return HitTarget(size, color);
case KickerComponent _: return Kicker(size, color);
case LightComponent _: return Light(size, color);
+ case LightGroupComponent _: return LightGroup(size, color);
case PlungerComponent _: return Plunger(size, color);
case PlayfieldComponent _: return Playfield(size, color);
case PrimitiveComponent _: return Primitive(size, color);
@@ -156,6 +162,7 @@ public static void DisableGizmoIcons()
DisableGizmo();
DisableGizmo();
DisableGizmo();
+ //DisableGizmo();
DisableGizmo();
DisableGizmo();
DisableGizmo();
@@ -169,6 +176,7 @@ public static void DisableGizmoIcons()
DisableGizmo();
DisableGizmo();
DisableGizmo();
+ DisableGizmo();
DisableGizmo();
DisableGizmo();
DisableGizmo();
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerInspector.cs
index e62a2d528..85f8c37b8 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerInspector.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerInspector.cs
@@ -16,8 +16,11 @@
// ReSharper disable AssignmentInConditionalExpression
+using System;
using System.Collections.Generic;
+using Unity.Mathematics;
using UnityEditor;
+using UnityEngine;
using VisualPinball.Engine.VPT;
using VisualPinball.Engine.VPT.Kicker;
@@ -85,5 +88,30 @@ public override void OnInspectorGUI()
EndEditing();
}
+
+ private void OnSceneGUI()
+ {
+ if (Event.current.type != EventType.Repaint) {
+ return;
+ }
+
+ Handles.color = Color.cyan;
+ var transform = MainComponent.transform;
+ var position = transform.parent.TransformPoint(MainComponent.GetEditorPosition());
+
+ foreach (var coil in MainComponent.Coils) {
+ var from = MainComponent.GetBallCreationPosition().ToUnityVector3();
+ var l = 20f * coil.Speed;
+ var dir = new Vector3(
+ l * math.cos(math.radians(coil.Angle)),
+ l * math.sin(math.radians(coil.Angle)),
+ l * math.sin(math.radians(coil.Inclination))
+ );
+ var to = from + dir;
+ var worldDir = transform.TransformDirection(math.normalize( to - from));
+
+ Handles.ArrowHandleCap(-1, position, Quaternion.LookRotation(worldDir), coil.Speed / 10f, EventType.Repaint);
+ }
+ }
}
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightExtensions.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightExtensions.cs
index fa8974bf4..ba3152d42 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightExtensions.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightExtensions.cs
@@ -15,14 +15,17 @@
// along with this program. If not, see .
using VisualPinball.Engine.VPT.Light;
+using VisualPinball.Engine.VPT.Table;
namespace VisualPinball.Unity.Editor
{
public static class LightExtensions
{
- internal static IVpxPrefab InstantiatePrefab(this Light light)
+ internal static IVpxPrefab InstantiatePrefab(this Light light, Table table)
{
- var prefab = RenderPipeline.Current.PrefabProvider.CreateLight();
+ var prefab = light.IsInsertLight(table)
+ ? RenderPipeline.Current.PrefabProvider.CreateInsertLight()
+ : RenderPipeline.Current.PrefabProvider.CreateLight();
return new VpxPrefab(prefab, light);
}
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightGroupInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightGroupInspector.cs
new file mode 100644
index 000000000..9598519bf
--- /dev/null
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Light/LightGroupInspector.cs
@@ -0,0 +1,90 @@
+// Visual Pinball Engine
+// Copyright (C) 2021 freezy and VPE Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// ReSharper disable AssignmentInConditionalExpression
+
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace VisualPinball.Unity.Editor
+{
+ [CustomEditor(typeof(LightGroupComponent)), CanEditMultipleObjects]
+ public class LightGroupInspector : ItemInspector
+ {
+ private LightGroupComponent _lightGroupComponent;
+ private SerializedProperty _lightGroupProperty;
+
+ protected override MonoBehaviour UndoTarget => target as MonoBehaviour;
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ _lightGroupComponent = target as LightGroupComponent;
+ _lightGroupProperty = serializedObject.FindProperty(nameof(LightGroupComponent.Lights));
+ }
+
+ public override void OnInspectorGUI()
+ {
+ BeginEditing();
+
+ OnPreInspectorGUI();
+
+ PropertyField(_lightGroupProperty, "Lights");
+
+ GUILayout.Space(10);
+ if (GUILayout.Button("Add Children")) {
+ var lights = _lightGroupComponent.GetComponentsInChildren();
+ foreach (var light in lights) {
+ if (_lightGroupComponent.Lights.ToList().Contains(light)) {
+ continue;
+ }
+ _lightGroupProperty.InsertArrayElementAtIndex(_lightGroupProperty.arraySize);
+ _lightGroupProperty.GetArrayElementAtIndex(_lightGroupProperty.arraySize - 1).objectReferenceValue = light;
+ }
+ }
+
+ if (GUILayout.Button("Replace With Children")) {
+ var lights = _lightGroupComponent.GetComponentsInChildren();
+ _lightGroupProperty.ClearArray();
+ foreach (var light in lights) {
+ _lightGroupProperty.InsertArrayElementAtIndex(_lightGroupProperty.arraySize);
+ _lightGroupProperty.GetArrayElementAtIndex(_lightGroupProperty.arraySize - 1).objectReferenceValue = light;
+ }
+ }
+
+ if (GUILayout.Button("Clear")) {
+ _lightGroupProperty.ClearArray();
+ }
+
+ base.OnInspectorGUI();
+
+ EndEditing();
+
+ GUILayout.Space(10);
+ if (GUILayout.Button("Select Light Sources")) {
+ var selection = new List
public string Ref;
+ public float FloatParam;
+
public abstract bool Matches(FileTableContainer tableContainer, GameObject obj);
}
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/NameMatchAttribute.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/NameMatchAttribute.cs
index c5e8fba01..ccb05b9c4 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/NameMatchAttribute.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/NameMatchAttribute.cs
@@ -16,7 +16,6 @@
using System;
using UnityEngine;
-using VisualPinball.Engine.Game;
using VisualPinball.Engine.VPT.Table;
namespace VisualPinball.Unity.Patcher
@@ -35,11 +34,14 @@ public NameMatchAttribute(string name)
_name = name;
}
- public override bool Matches(FileTableContainer tableContainer, GameObject obj)
+ public override bool Matches(FileTableContainer tableContainer, GameObject go)
{
+ if (!go) {
+ return false;
+ }
return IgnoreCase
- ? string.Equals(obj.name, _name, StringComparison.CurrentCultureIgnoreCase)
- : obj.name == _name;
+ ? string.Equals(go.name, _name, StringComparison.CurrentCultureIgnoreCase)
+ : go.name == _name;
}
}
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/RenderPipelineAttribute.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/RenderPipelineAttribute.cs
index 062e63532..cbe9baaee 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/RenderPipelineAttribute.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/Item/RenderPipelineAttribute.cs
@@ -15,7 +15,6 @@
// along with this program. If not, see .
using UnityEngine;
-using VisualPinball.Engine.Game;
using VisualPinball.Engine.VPT.Table;
namespace VisualPinball.Unity.Patcher.Matcher.Item
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs
new file mode 100644
index 000000000..6368230fe
--- /dev/null
+++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs
@@ -0,0 +1,430 @@
+// Visual Pinball Engine
+// Copyright (C) 2021 freezy and VPE Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using VisualPinball.Engine.VPT;
+using VisualPinball.Engine.VPT.Light;
+using VisualPinball.Engine.VPT.Table;
+using VisualPinball.Engine.VPT.Trough;
+using VisualPinball.Unity.Editor;
+using Light = UnityEngine.Light;
+using Object = UnityEngine.Object;
+
+namespace VisualPinball.Unity.Patcher
+{
+ [Api]
+ public abstract class TablePatcher
+ {
+ public TableContainer TableContainer;
+ public ITextureProvider TextureProvider;
+ public IMaterialProvider MaterialProvider;
+
+ ///
+ /// This method is executed once after all element-specific patches had
+ /// been applied.
+ ///
+ /// Override this method when you need to create new objects or make global
+ /// changes to the project.
+ ///
+ /// GameObject of the table.
+ public virtual void PostPatch(GameObject tableGo)
+ {
+ CreateTrough(tableGo, Playfield(tableGo));
+ }
+
+ #region GameObject Helpers
+
+ ///
+ /// Returns the playfield of a given table game object.
+ ///
+ /// Table game object
+ ///
+ protected static GameObject Playfield(GameObject tableGo)
+ {
+ var pf = tableGo.GetComponentInChildren();
+ if (pf) {
+ return pf.gameObject;
+ }
+
+ Debug.LogWarning($"Cannot find playfield of \"{tableGo.name}\".");
+ return tableGo;
+
+ }
+
+ ///
+ /// Creates an empty game object.
+ ///
+ /// Parent of the new game object
+ /// Name of the new game object
+ ///
+ protected static GameObject CreateEmptyGameObject(GameObject parentGo, string name)
+ {
+ var newGo = new GameObject(name);
+ newGo.transform.SetParent(parentGo.transform, false);
+ return newGo;
+ }
+
+ protected static GameObject GetOrCreateGameObject(GameObject parentGo, string name)
+ {
+ for (var i = 0; i < parentGo.transform.childCount; i++) {
+ if (parentGo.transform.GetChild(i).gameObject.name == name) {
+ return parentGo.transform.GetChild(i).gameObject;
+ }
+ }
+
+ return CreateEmptyGameObject(parentGo, name);
+ }
+
+ #endregion
+
+ #region Element Helpers
+
+ ///
+ /// Creates a trough.
+ ///
+ /// Table game object, for retrieving references
+ /// Parent game object of the new trough
+ /// Name of the new trough
+ /// Name of the exit kicker
+ /// Name of the entry switch
+ protected static TroughComponent CreateTrough(GameObject tableGo, GameObject parentGo,
+ string name = "Trough", string exitKicker = "BallRelease", string entrySwitch = "Drain")
+ {
+ var trough = new Trough(new TroughData {
+ BallCount = 4,
+ SwitchCount = 4,
+ Type = TroughType.ModernMech,
+ });
+
+ var troughGo = trough.InstantiateEditorPrefab(parentGo.transform);
+ var troughComponent = troughGo.GetComponent();
+
+ var kickers = tableGo.GetComponentsInChildren();
+ foreach (var kicker in kickers) {
+ if (string.Equals(kicker.name, exitKicker, StringComparison.OrdinalIgnoreCase)) {
+ troughComponent.PlayfieldExitKicker = kicker;
+ troughComponent.PlayfieldExitKickerItem = kicker.AvailableCoils.First().Id;
+ }
+ if (string.Equals(kicker.name, entrySwitch, StringComparison.OrdinalIgnoreCase)) {
+ troughComponent.PlayfieldEntrySwitch = kicker;
+ troughComponent.PlayfieldEntrySwitchItem = kicker.AvailableSwitches.First().Id;
+ }
+ }
+
+ if (troughComponent.PlayfieldEntrySwitch == null) {
+ var triggers = tableGo.GetComponentsInChildren();
+ foreach (var trigger in triggers) {
+
+ if (string.Equals(trigger.name, entrySwitch, StringComparison.OrdinalIgnoreCase)) {
+ troughComponent.PlayfieldEntrySwitch = trigger;
+ }
+ }
+ }
+
+ troughGo.name = name;
+ return troughComponent;
+ }
+
+ #endregion
+
+ #region Light Helpers
+
+ ///
+ /// Adds a light group component to an existing game object.
+ ///
+ /// Table game object for retrieving light references.
+ /// Game object to which the light group is added to.
+ /// A list of light names that are part of the light group.
+ protected static LightGroupComponent AddLightGroup(GameObject tableGo, GameObject go, params string[] names)
+ {
+ var nameIndex = new HashSet(names);
+ var lightComponentGroup = go.AddComponent();
+ var lights = tableGo
+ .GetComponentsInChildren()
+ .Where(lc => nameIndex.Contains(lc.name));
+ lightComponentGroup.Lights = lights.ToArray();
+
+ return lightComponentGroup;
+ }
+
+ ///
+ /// Sets the light color of a light source.
+ ///
+ ///
+ /// Supports multiple light sources.
+ ///
+ /// Game object of the light source
+ /// New color of the light source
+ protected static void LightColor(GameObject go, Color color)
+ {
+ var lights = go.GetComponentsInChildren();
+ foreach (var light in lights) {
+ RenderPipeline.Current.LightConverter.SetColor(light, color);
+ }
+ }
+
+ ///
+ /// Sets the temperature of the light.
+ ///
+ /// Game object of the light
+ /// Temperature in Kelvin
+ protected static void LightTemperature(GameObject go, float temp)
+ {
+ foreach (var l in go.GetComponentsInChildren()) {
+ RenderPipeline.Current.LightConverter.SetTemperature(l, temp);
+ }
+ }
+
+ ///
+ /// Sets the angle of a spot light.
+ ///
+ ///
+ /// Supports multiple light sources.
+ ///
+ /// Game object of the spot light
+ /// Outer angle of the spot
+ /// Inner angle of the spot, in percent of the outer angle
+ protected static void SpotAngle(GameObject go, float outer, float inner)
+ {
+ var lights = go.GetComponentsInChildren();
+ foreach (var light in lights) {
+ RenderPipeline.Current.LightConverter.SpotLight(light, outer, inner);
+ }
+ }
+
+ ///
+ /// Sets a light source to pyramid spotlight and sets its parameters.
+ ///
+ ///
+ /// Supports multiple light sources.
+ ///
+ /// Game object of the light source
+ /// Angle of the pyramid
+ /// Aspect ratio of the pyramid
+ protected static void PyramidAngle(GameObject go, float angle, float ar)
+ {
+ var lights = go.GetComponentsInChildren();
+ foreach (var light in lights) {
+ RenderPipeline.Current.LightConverter.PyramidAngle(light, angle, ar);
+ }
+ }
+
+ ///
+ /// Sets the intensity of a light source in lumen.
+ ///
+ ///
+ /// Supports multiple light sources.
+ ///
+ /// Game object of the light source
+ /// Intensity of the light in lumen
+ protected static void LightIntensity(GameObject go, float intensityLumen)
+ {
+ var lights = go.GetComponentsInChildren();
+ foreach (var light in lights) {
+ RenderPipeline.Current.LightConverter.SetIntensity(light, intensityLumen);
+ }
+ }
+
+ ///
+ /// Sets the range of the light.
+ ///
+ /// Game object of the light
+ /// Range in meters
+ protected static void LightRange(GameObject go, float range)
+ {
+ var lights = go.GetComponentsInChildren();
+ foreach (var light in lights) {
+ RenderPipeline.Current.LightConverter.SetRange(light, range);
+ }
+ }
+
+ ///
+ /// Sets the shadow of the light.
+ ///
+ /// Game object of the light
+ /// Whether to enable or disable shadows.
+ /// If true, update on each frame.
+ /// Distance from when on shadows are cast.
+ protected static void LightShadow(GameObject go, bool enabled, bool isDynamic, float nearPlane = 0.01f)
+ {
+ var lights = go.GetComponentsInChildren();
+ foreach (var light in lights) {
+ RenderPipeline.Current.LightConverter.SetShadow(light, enabled, isDynamic, nearPlane);
+ }
+ }
+
+ ///
+ /// Sets the light position.
+ ///
+ ///
+ /// Supports multiple light sources. Note that this only applies to the light source, not to the light itself.
+ ///
+ /// Game object of the light source
+ /// X-position of the source relative to the light
+ /// Y-position of the source relative to the light
+ /// Z-position of the source relative to the light
+ protected static void LightPos(GameObject go, float x, float y, float z)
+ {
+ var light = go.GetComponentInChildren();
+ if (light != null) {
+ light.gameObject.transform.localPosition = new Vector3(x, y, z);
+ }
+ }
+
+ ///
+ /// Creates a point light.
+ ///
+ /// Name of the new light
+ /// X-position on the playfield
+ /// Y-position on the playfield
+ /// Game object to parent to (usually "Lights")
+ ///
+ protected static LightComponent CreateLight(string name, float x, float y, GameObject parentGo)
+ {
+ var light = Engine.VPT.Light.Light.GetDefault(name, x, y);
+ light.Data.ShowBulbMesh = false;
+
+ var prefab = RenderPipeline.Current.PrefabProvider.CreateLight();
+ var lightGo = PrefabUtility.InstantiatePrefab(prefab, parentGo.transform) as GameObject;
+ if (!lightGo) {
+ return null;
+ }
+ lightGo.name = name;
+ var lightTransform = lightGo.transform;
+ var lightComponent = lightGo.GetComponent();
+ lightComponent.SetData(light.Data);
+ lightComponent.UpdateTransforms();
+ lightTransform.Find("Bulb").gameObject.SetActive(false);
+ lightTransform.Find("Socket").gameObject.SetActive(false);
+ return lightComponent;
+ }
+
+ ///
+ /// Converts a normal light to an insert light, by deleting and re-creating the insert prefab.
+ ///
+ /// Light component to convert
+ /// New converted game object
+ protected GameObject ConvertToInsertLight(LightComponent lo)
+ {
+ var name = lo.name;
+ var parent = lo.transform.parent.gameObject;
+ Object.DestroyImmediate(lo.gameObject);
+ return CreateInsertLight(TableContainer.Get(name).Data, parent);
+ }
+
+ ///
+ /// Creates an insert light based on existing light data.
+ ///
+ ///
+ ///
+ private GameObject CreateInsertLight(LightData data, GameObject parentGo)
+ {
+ var prefab = RenderPipeline.Current.PrefabProvider.CreateInsertLight();
+ var go = PrefabUtility.InstantiatePrefab(prefab, parentGo.transform) as GameObject;
+ go!.name = data.Name;
+ data.OffImage = TableContainer.Table.Data.Image;
+ var lc = go.GetComponent();
+ lc.SetData(data);
+ lc.SetReferencedData(data, TableContainer.Table, MaterialProvider, TextureProvider, null);
+
+ EditorUtility.SetDirty(go);
+ PrefabUtility.RecordPrefabInstancePropertyModifications(lc);
+
+ return go;
+ }
+
+ ///
+ /// Duplicates a light source.
+ ///
+ /// Game object of the light source
+ /// X-position of the new light source, relative to the lamp
+ /// Y-position of the new light source, relative to the lamp
+ /// Z-position of the new light source, relative to the lamp
+ protected static void DuplicateLight(GameObject go, float x, float y, float z)
+ {
+ var light = go.GetComponentInChildren();
+ if (light != null) {
+ var newGo = Object.Instantiate(light.gameObject, go.transform, true);
+ newGo.transform.localPosition = new Vector3(x, y, z);
+ }
+ }
+
+ ///
+ /// Creates a light group with the given light names
+ ///
+ /// GameObject to add the light group to.
+ /// Names of the light GameObjects. They must be sister objects of the first parameter.
+ protected static void LinkLights(GameObject go, params string[] lightNames)
+ {
+ var parentTransform = go.transform.parent;
+ var lightComponents = lightNames
+ .Select(n => parentTransform.Find(n).GetComponent())
+ .Where(c => c != null);
+ var lg = go.AddComponent();
+ lg.Lights = lightComponents.ToArray();
+ }
+
+ #endregion
+
+ #region Mapping Helpers
+
+ ///
+ /// Links a coil device to an existing coil mapping if it matches a given name.
+ ///
+ /// Table component for retrieving mappings.
+ /// The name that the coil device's GameObject has to match in order to be linked.
+ /// The ID of the coil mapping that the coil device will be linked to
+ /// The coil device to be linked
+ protected static void LinkCoil(TableComponent tableComponent, string elementName, string coilId, ICoilDeviceComponent coilDevice)
+ {
+ if (!string.Equals(coilDevice.gameObject.name, elementName, StringComparison.OrdinalIgnoreCase)) {
+ return;
+ }
+ var coilMapping = tableComponent.MappingConfig.Coils.FirstOrDefault(cm => cm.Id == coilId);
+ if (coilMapping == null) {
+ return;
+ }
+ coilMapping.Device = coilDevice;
+ coilMapping.DeviceItem = coilDevice.AvailableCoils.First().Id;
+ }
+
+ ///
+ /// Links a switch device to an existing switch mapping if it matches a given name.
+ ///
+ /// Table component for retrieving mappings.
+ /// The name that the switch device's GameObject has to match in order to be linked.
+ /// The ID of the switch mapping that the switch device will be linked to
+ /// The switch device to be linked
+ protected static void LinkSwitch(TableComponent tableComponent, string elementName, string switchId, ISwitchDeviceComponent switchDevice)
+ {
+ if (!string.Equals(switchDevice.gameObject.name, elementName, StringComparison.OrdinalIgnoreCase)) {
+ return;
+ }
+ var switchMapping = tableComponent.MappingConfig.Switches.FirstOrDefault(sw => sw.Id == switchId);
+ if (switchMapping == null) {
+ return;
+ }
+ switchMapping.Device = switchDevice;
+ switchMapping.DeviceItem = switchDevice.AvailableSwitches.First().Id;
+ }
+
+ #endregion
+ }
+}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs.meta
new file mode 100644
index 000000000..dc0d95ed9
--- /dev/null
+++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f2db227803e55c64fac564f27a2464b2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Patcher.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Patcher.cs
index e0e3ab6f5..521bd560e 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Patcher.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Patcher.cs
@@ -22,23 +22,27 @@
using System.Reflection;
using NLog;
using UnityEngine;
-using VisualPinball.Engine.Game;
-using VisualPinball.Engine.VPT;
using VisualPinball.Engine.VPT.Table;
using Logger = NLog.Logger;
namespace VisualPinball.Unity.Patcher
{
- public class Patcher : IPatcher
+ public sealed class Patcher : IPatcher
{
private readonly List