using CW.Common; using PaintCore; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace game { /// This class contains common code for screen based mouse/finger hit components. public abstract class BHitScreenBase : CwHitPointers { public enum RotationType { Normal, World, ThisRotation, ThisLocalRotation, CustomRotation, CustomLocalRotation } public enum RelativeType { WorldUp, CameraUp, DrawAngle } public enum DirectionType { HitNormal, RayDirection, CameraDirection } public enum EmitType { PointsIn3D = 0, PointsOnUV = 20, TrianglesIn3D = 30 } [System.Flags] public enum ButtonTypes { LeftMouse = 1 << 0, RightMouse = 1 << 1, MiddleMouse = 1 << 2, Touch = 1 << 5 } /// Orient to a specific camera? /// None = MainCamera. public Camera Camera { set { _camera = value; } get { return _camera; } } [SerializeField] private Camera _camera; /// The layers you want the raycast to hit. public LayerMask Layers { set { layers = value; } get { return layers; } } [SerializeField] private LayerMask layers = Physics.DefaultRaycastLayers; /// This allows you to control the hit data this component sends out. /// PointsIn3D = Point drawing in 3D. /// PointsOnUV = Point drawing on UV (requires non-convex MeshCollider). /// TrianglesIn3D = Triangle drawing in 3D (requires non-convex MeshCollider). public EmitType Emit { set { emit = value; } get { return emit; } } [UnityEngine.Serialization.FormerlySerializedAs("draw")][SerializeField] private EmitType emit; /// This allows you to control how the paint is rotated. /// Normal = The rotation will be based on a normal direction, and rolled relative to an up axis. /// World = The rotation will be aligned to the world, or given no rotation. /// ThisRotation = The current Transform.rotation will be used. /// ThisLocalRotation = The current Transform.localRotation will be used. /// CustomRotation = The specified CustomTransform.rotation will be used. /// CustomLocalRotation = The specified CustomTransform.localRotation will be used. public RotationType RotateTo { set { rotateTo = value; } get { return rotateTo; } } [SerializeField] private RotationType rotateTo; /// Which direction should the hit point rotation be based on? public DirectionType NormalDirection { set { normalDirection = value; } get { return normalDirection; } } [UnityEngine.Serialization.FormerlySerializedAs("normal")][SerializeField] private DirectionType normalDirection = DirectionType.CameraDirection; /// Based on the normal direction, what should the rotation be rolled relative to? /// WorldUp = It will be rolled so the up vector is world up. /// CameraUp = It will be rolled so the up vector is camera up. /// DrawAngle = It will be rolled according to the mouse/finger movement on screen. public RelativeType NormalRelativeTo { set { normalRelativeTo = value; } get { return normalRelativeTo; } } [UnityEngine.Serialization.FormerlySerializedAs("orientation")][SerializeField] private RelativeType normalRelativeTo = RelativeType.CameraUp; /// This allows you to specify the Transform when using RotateTo = CustomRotation/CustomLocalRotation. public Transform CustomTransform { set { customTransform = value; } get { return customTransform; } } [SerializeField] private Transform customTransform; /// Should painting triggered from this component be eligible for being undone? public bool StoreStates { set { storeStates = value; } get { return storeStates; } } [SerializeField] protected bool storeStates = true; /// This allows you to override the order this paint gets applied to the object during the current frame. public int Priority { set { priority = value; } get { return priority; } } [SerializeField] private int priority; /// If you want the raycast hit point to be offset from the surface a bit, this allows you to set by how much in world space. public float NormalOffset { set { normalOffset = value; } get { return normalOffset; } } [SerializeField] private float normalOffset; // No longer used [SerializeField] private ButtonTypes requiredButtons; // No longer used [SerializeField] private KeyCode requiredKey; // No longer used [SerializeField] private float mouseOffset; // No longer used [SerializeField] private float touchOffset; // No longer used [SerializeField] private bool showPreview; private static Transform m_TargetTransform = null; public static void SetTargetTransform(Transform targetTransform) { m_TargetTransform = targetTransform; } public bool NeedsDrawAngle { get { return rotateTo == RotationType.Normal && normalRelativeTo == RelativeType.DrawAngle; } } private bool m_IsAuthorizedDrawing = false; public void AuthorizedDrawing() { m_IsAuthorizedDrawing = true; } public void NotAuthorizedDrawing() { m_IsAuthorizedDrawing = false; } #if UNITY_EDITOR protected virtual void Reset() { if (GetComponent() == null) { requiredButtons &= ~ButtonTypes.LeftMouse; requiredButtons &= ~ButtonTypes.RightMouse; requiredButtons &= ~ButtonTypes.MiddleMouse; gameObject.AddComponent(); } if (GetComponent() == null) { requiredButtons &= ~ButtonTypes.Touch; gameObject.AddComponent(); } if (GetComponent() == null) { gameObject.AddComponent(); } } #endif public bool ShouldUpgradePointers() { return (requiredButtons & ButtonTypes.LeftMouse) != 0 || (requiredButtons & ButtonTypes.RightMouse) != 0 || (requiredButtons & ButtonTypes.MiddleMouse) != 0 || (requiredButtons & ButtonTypes.Touch) != 0; } public void TryUpgradePointers() { var lmb = (requiredButtons & ButtonTypes.LeftMouse) != 0; var rmb = (requiredButtons & ButtonTypes.RightMouse) != 0; var mmb = (requiredButtons & ButtonTypes.MiddleMouse) != 0; if (lmb == true || rmb == true || mmb == true) { requiredButtons &= ~ButtonTypes.LeftMouse; requiredButtons &= ~ButtonTypes.RightMouse; requiredButtons &= ~ButtonTypes.MiddleMouse; var pointerMouse = gameObject.AddComponent(); pointerMouse.Preview = showPreview; if (lmb == true) { pointerMouse.TryAddKey(KeyCode.Mouse0); } if (rmb == true) { pointerMouse.TryAddKey(KeyCode.Mouse1); } if (mmb == true) { pointerMouse.TryAddKey(KeyCode.Mouse2); } } if ((requiredButtons & ButtonTypes.Touch) != 0) { requiredButtons &= ~ButtonTypes.Touch; var pointerTouch = gameObject.AddComponent(); pointerTouch.Offset = touchOffset; } } protected virtual void DoQuery(Vector2 screenPosition, ref Camera camera, ref Ray ray, ref CwHit hit3D, ref RaycastHit2D hit2D) { var hit = default(RaycastHit); camera = CwHelper.GetCamera(_camera); ray = camera.ScreenPointToRay(screenPosition); hit2D = Physics2D.GetRayIntersection(ray, float.PositiveInfinity, layers); Physics.Raycast(ray, out hit, float.PositiveInfinity, layers); hit3D = new CwHit(hit); } protected void PaintAt(CwPointConnector connector, CwHitCache hitCache, Vector2 screenPosition, Vector2 screenPositionOld, bool preview, float pressure, object owner) { var camera = default(Camera); var ray = default(Ray); var hit2D = default(RaycastHit2D); var hit3D = default(CwHit); var finalPosition = default(Vector3); var finalRotation = default(Quaternion); DoQuery(screenPosition, ref camera, ref ray, ref hit3D, ref hit2D); var valid2D = hit2D.distance > 0.0f; var valid3D = hit3D.Distance > 0.0f; // Hit 3D? if (valid3D == true && (valid2D == false || hit3D.Distance < hit2D.distance)) { CalcHitData(hit3D.Position, hit3D.Normal, ray, camera, screenPositionOld, ref finalPosition, ref finalRotation); if (emit == EmitType.PointsIn3D) { if (connector != null) { if (m_TargetTransform != null && hit3D.Transform != m_TargetTransform) { return; } if (m_IsAuthorizedDrawing) { connector.SubmitPoint(gameObject, preview, priority, pressure, finalPosition, finalRotation, owner); } FishingYachtAct.Publish(new EventWashingCleanerFollowPositionData(finalPosition, hit3D.Normal)); } else { hitCache.InvokePoint(gameObject, preview, priority, pressure, finalPosition, finalRotation); } return; } else if (emit == EmitType.PointsOnUV) { hitCache.InvokeCoord(gameObject, preview, priority, pressure, hit3D, finalRotation); return; } else if (emit == EmitType.TrianglesIn3D) { hitCache.InvokeTriangle(gameObject, preview, priority, pressure, hit3D, finalRotation); return; } } // Hit 2D? else if (valid2D == true) { CalcHitData(hit2D.point, new Vector3(0.0f, 0.0f, -1.0f), ray, camera, screenPositionOld, ref finalPosition, ref finalRotation); if (emit == EmitType.PointsIn3D) { if (connector != null) { connector.SubmitPoint(gameObject, preview, priority, pressure, finalPosition, finalRotation, owner); } else { hitCache.InvokePoint(gameObject, preview, priority, pressure, finalPosition, finalRotation); } return; } } if (connector != null) { connector.BreakHits(owner); } } private void CalcHitData(Vector3 hitPoint, Vector3 hitNormal, Ray ray, Camera camera, Vector2 screenPositionOld, ref Vector3 finalPosition, ref Quaternion finalRotation) { finalPosition = hitPoint + hitNormal * normalOffset; finalRotation = Quaternion.identity; switch (rotateTo) { case RotationType.Normal: { var finalNormal = default(Vector3); switch (normalDirection) { case DirectionType.HitNormal: finalNormal = hitNormal; break; case DirectionType.RayDirection: finalNormal = -ray.direction; break; case DirectionType.CameraDirection: finalNormal = -camera.transform.forward; break; } var finalUp = Vector3.up; switch (normalRelativeTo) { case RelativeType.CameraUp: finalUp = camera.transform.up; break; case RelativeType.DrawAngle: { var rayOld = camera.ScreenPointToRay(screenPositionOld); if (camera.orthographic == true) { finalUp = Vector3.Cross(rayOld.GetPoint(1.0f) - ray.origin, ray.GetPoint(1.0f) - ray.origin); } else { finalUp = Vector3.Cross(rayOld.direction, ray.direction); } } break; } finalRotation = Quaternion.LookRotation(-finalNormal, finalUp); } break; case RotationType.World: finalRotation = Quaternion.identity; break; case RotationType.ThisRotation: finalRotation = transform.rotation; break; case RotationType.ThisLocalRotation: finalRotation = transform.localRotation; break; case RotationType.CustomRotation: if (customTransform != null) finalRotation = customTransform.rotation; break; case RotationType.CustomLocalRotation: if (customTransform != null) finalRotation = customTransform.localRotation; break; } } } }