public UnityEngine.Material CreateMaterial(PbrMaterial vpxMaterial, TableBehavior table, StringBuilder debug = null)
        {
            var unityMaterial = new UnityEngine.Material(GetShader())
            {
                name = vpxMaterial.Id
            };

            // apply some basic manipulations to the color. this just makes very
            // very white colors be clipped to 0.8204 aka 204/255 is 0.8
            // this is to give room to lighting values. so there is more modulation
            // of brighter colors when being lit without blow outs too soon.
            var col = vpxMaterial.Color.ToUnityColor();

            if (vpxMaterial.Color.IsGray() && col.grayscale > 0.8)
            {
                debug?.AppendLine("Color manipulation performed, brightness reduced.");
                col.r = col.g = col.b = 0.8f;
            }
            unityMaterial.SetColor(Color, col);

            // validate IsMetal. if true, set the metallic value.
            // found VPX authors setting metallic as well as translucent at the
            // same time, which does not render correctly in unity so we have
            // to check if this value is true and also if opacity <= 1.
            if (vpxMaterial.IsMetal && (!vpxMaterial.IsOpacityActive || vpxMaterial.Opacity >= 1))
            {
                unityMaterial.SetFloat(Metallic, 1f);
                debug?.AppendLine("Metallic set to 1.");
            }

            // roughness / glossiness
            unityMaterial.SetFloat(Glossiness, vpxMaterial.Roughness);

            // blend mode
            ApplyBlendMode(unityMaterial, vpxMaterial.MapBlendMode);
            if (vpxMaterial.MapBlendMode == BlendMode.Translucent)
            {
                col.a = Mathf.Min(1, Mathf.Max(0, vpxMaterial.Opacity));
                unityMaterial.SetColor(Color, col);
            }

            // map
            if (table != null && vpxMaterial.HasMap)
            {
                unityMaterial.SetTexture(
                    MainTex,
                    table.GetTexture(vpxMaterial.Map.Name)
                    );
            }

            // normal map
            if (table != null && vpxMaterial.HasNormalMap)
            {
                unityMaterial.EnableKeyword("_NORMALMAP");
                unityMaterial.SetTexture(
                    BumpMap,
                    table.GetTexture(vpxMaterial.NormalMap.Name)
                    );
            }

            return(unityMaterial);
        }
        public static GameObject ConvertRenderObjects(IRenderable item, RenderObjectGroup rog, GameObject parent, TableBehavior tb)
        {
            var obj = new GameObject(rog.Name);

            obj.transform.parent = parent.transform;

            if (rog.HasOnlyChild && !rog.ForceChild)
            {
                ConvertRenderObject(item, rog.RenderObjects[0], obj, tb);
            }
            else if (rog.HasChildren)
            {
                foreach (var ro in rog.RenderObjects)
                {
                    var subObj = new GameObject(ro.Name);
                    subObj.transform.SetParent(obj.transform, false);
                    subObj.layer = ChildObjectsLayer;
                    ConvertRenderObject(item, ro, subObj, tb);
                }
            }

            // apply transformation
            obj.transform.SetFromMatrix(rog.TransformationMatrix.ToUnityMatrix());

            // add unity component
            MonoBehaviour ic = null;

            switch (item)
            {
            case Bumper bumper:                                     ic = bumper.SetupGameObject(obj, rog); break;

            case Flipper flipper:                           ic = flipper.SetupGameObject(obj, rog); break;

            case Gate gate:                                         ic = gate.SetupGameObject(obj, rog); break;

            case HitTarget hitTarget:                       ic = hitTarget.SetupGameObject(obj, rog); break;

            case Kicker kicker:                                     ic = kicker.SetupGameObject(obj, rog); break;

            case Engine.VPT.Light.Light lt:         ic = lt.SetupGameObject(obj, rog); break;

            case Plunger plunger:                           ic = plunger.SetupGameObject(obj, rog); break;

            case Primitive primitive:                       ic = obj.AddComponent <PrimitiveBehavior>().SetItem(primitive); break;

            case Ramp ramp:                                         ic = ramp.SetupGameObject(obj, rog); break;

            case Rubber rubber:                                     ic = rubber.SetupGameObject(obj, rog); break;

            case Spinner spinner:                           ic = spinner.SetupGameObject(obj, rog); break;

            case Surface surface:                           ic = surface.SetupGameObject(obj, rog); break;

            case Table table:                                       ic = table.SetupGameObject(obj, rog); break;

            case Trigger trigger:                           ic = trigger.SetupGameObject(obj, rog); break;
            }
#if UNITY_EDITOR
            // for convenience move item behavior to the top of the list
            if (ic != null)
            {
                int numComp = obj.GetComponents <MonoBehaviour>().Length;
                for (int i = 0; i <= numComp; i++)
                {
                    UnityEditorInternal.ComponentUtility.MoveComponentUp(ic);
                }
            }
#endif
            return(obj);
        }
        public static UnityEngine.Material ToUnityMaterial(this PbrMaterial vpxMaterial, TableBehavior table, StringBuilder debug = null)
        {
            if (table != null)
            {
                var existingMat = table.GetMaterial(vpxMaterial);
                if (existingMat != null)
                {
                    return(existingMat);
                }
            }

            var unityMaterial = MaterialConverter.CreateMaterial(vpxMaterial, table, debug);

            if (table != null)
            {
                table.AddMaterial(vpxMaterial, unityMaterial);
            }

            return(unityMaterial);
        }
        public static void ConvertRenderObject(IRenderable item, RenderObject ro, GameObject obj, TableBehavior table)
        {
            if (ro.Mesh == null)
            {
                Logger.Warn($"No mesh for object {obj.name}, skipping.");
                return;
            }

            var mesh = ro.Mesh.ToUnityMesh($"{obj.name}_mesh");

            // apply mesh to game object
            var mf = obj.AddComponent <MeshFilter>();

            mf.sharedMesh = mesh;

            // apply material
            var mr = obj.AddComponent <MeshRenderer>();

            mr.sharedMaterial = ro.Material.ToUnityMaterial(table);
            mr.enabled        = ro.IsVisible;

            // patch
            table.Patcher?.ApplyPatches(item, ro, obj);
        }
        public UnityEngine.Material CreateMaterial(PbrMaterial vpxMaterial, TableBehavior table, StringBuilder debug = null)
        {
            var unityMaterial = new UnityEngine.Material(GetShader())
            {
                name = vpxMaterial.Id
            };

            // apply some basic manipulations to the color. this just makes very
            // very white colors be clipped to 0.8204 aka 204/255 is 0.8
            // this is to give room to lighting values. so there is more modulation
            // of brighter colors when being lit without blow outs too soon.
            var col = vpxMaterial.Color.ToUnityColor();

            if (vpxMaterial.Color.IsGray() && col.grayscale > 0.8)
            {
                debug?.AppendLine("Color manipulation performed, brightness reduced.");
                col.r = col.g = col.b = 0.8f;
            }

            // alpha for color depending on blend mode
            ApplyBlendMode(unityMaterial, vpxMaterial.MapBlendMode);
            if (vpxMaterial.MapBlendMode == Engine.VPT.BlendMode.Translucent)
            {
                col.a = Mathf.Min(1, Mathf.Max(0, vpxMaterial.Opacity));
            }
            unityMaterial.SetColor(BaseColor, col);

            // validate IsMetal. if true, set the metallic value.
            // found VPX authors setting metallic as well as translucent at the
            // same time, which does not render correctly in unity so we have
            // to check if this value is true and also if opacity <= 1.
            if (vpxMaterial.IsMetal && (!vpxMaterial.IsOpacityActive || vpxMaterial.Opacity >= 1))
            {
                unityMaterial.SetFloat(Metallic, 1f);
                debug?.AppendLine("Metallic set to 1.");
            }

            // roughness / glossiness
            unityMaterial.SetFloat(Smoothness, vpxMaterial.Roughness);

            // map
            if (table != null && vpxMaterial.HasMap)
            {
                unityMaterial.SetTexture(BaseColorMap, table.GetTexture(vpxMaterial.Map.Name));
            }

            // normal map
            if (table != null && vpxMaterial.HasNormalMap)
            {
                unityMaterial.EnableKeyword("_NORMALMAP");
                unityMaterial.EnableKeyword("_NORMALMAP_TANGENT_SPACE");

                unityMaterial.SetInt(NormalMapSpace, 0);                 // 0 = TangentSpace, 1 = ObjectSpace
                unityMaterial.SetFloat(NormalScale, 0f);                 // TODO FIXME: setting the scale to 0 for now. anything above 0 makes the entire unity editor window become black which is more likely a unity bug

                unityMaterial.SetTexture(NormalMap, table.GetTexture(vpxMaterial.NormalMap.Name));
            }

            // GI hack. This is a necessary step, see respective code in BaseUnlitGUI.cs of the HDRP source
            SetupMainTexForAlphaTestGI(unityMaterial, "_BaseColorMap", "_BaseColor");

            return(unityMaterial);
        }