protected override bool Initialize()
        {
            if (Material1 == null || Material2 == null)
            {
                Debug.LogWarning(name + ": Trying to create contact material with at least one unreferenced ShapeMaterial.", this);
                return(false);
            }

            agx.Material        m1  = Material1.GetInitialized <ShapeMaterial>().Native;
            agx.Material        m2  = Material2.GetInitialized <ShapeMaterial>().Native;
            agx.ContactMaterial old = GetSimulation().getMaterialManager().getContactMaterial(m1, m2);
            if (old != null)
            {
                Debug.LogWarning(name + ": Material manager already contains a contact material with this material pair. Ignoring this contact material.", this);
                return(false);
            }

            m_contactMaterial = GetSimulation().getMaterialManager().getOrCreateContactMaterial(m1, m2);

            if (FrictionModel != null)
            {
                m_contactMaterial.setFrictionModel(FrictionModel.GetInitialized <FrictionModel>().Native);
                // When the user changes friction model type (enum = BoxFriction, ScaleBoxFriction etc.)
                // the friction model object will create a new native instance. We'll receive callbacks
                // when this happens so we can assign it to our native contact material.
                FrictionModel.OnNativeInstanceChanged += OnFrictionModelNativeInstanceChanged;
            }

            return(true);
        }
        public void InitializeOrientedFriction(bool isOriented,
                                               GameObject referenceObject,
                                               FrictionModel.PrimaryDirection primaryDirection)
        {
            if (!isOriented || referenceObject == null || FrictionModel == null)
            {
                return;
            }

            if (!Application.isPlaying)
            {
                Debug.LogError("Oriented friction: Invalid to initialize oriented friction in edit mode.", this);
                return;
            }

            if (GetInitialized <ContactMaterial>() == null)
            {
                return;
            }

            if (FrictionModel.GetInitialized <FrictionModel>() == null)
            {
                return;
            }

            var rb    = referenceObject.GetComponent <RigidBody>();
            var shape = rb == null?
                        referenceObject.GetComponent <Collide.Shape>() :
                            null;

            var observer = rb == null && shape == null?
                           referenceObject.GetComponent <ObserverFrame>() :
                               null;

            agx.Frame referenceFrame = null;
            if (rb != null && rb.GetInitialized <RigidBody>() != null)
            {
                referenceFrame = rb.Native.getFrame();
            }
            else if (shape != null && shape.GetInitialized <Collide.Shape>() != null)
            {
                referenceFrame = shape.NativeGeometry.getFrame();
            }
            else if (observer != null && observer.GetInitialized <ObserverFrame>() != null)
            {
                referenceFrame = observer.Native.getFrame();
            }

            if (referenceFrame == null)
            {
                Debug.LogWarning($"Oriented friction: Unable to find reference frame from {referenceObject.name}.", referenceObject);
                return;
            }

            if (rb != null)
            {
                FrictionModel.InitializeOriented(rb, primaryDirection);
            }
            else
            {
                FrictionModel.InitializeOriented(shape, primaryDirection);
            }
        }