Unity Object Painter Tool Script

Previous Page: Back

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

#if UNITY_EDITOR
public class ObjectPainter : EditorWindow
{

    [MenuItem("My Tools/Object Painter")]
    public static void OpenWindow() => GetWindow<ObjectPainter>();

    public float radius = 2f;
    public int spawnCount = 8;

    private float raycastOriginDistance = 2.0f;

    public GameObject spawnPrefab;

    public bool enablePainting;
    public bool singleObjectMode;
    public bool useRandomRotations;
    public bool shouldEmbed;

    public float embedAmount = 1.0f;

    Vector2[] randomPoints; //Random points in disk surrounding center
    List<Vector3> objectPoints;
    List<Vector3> objectNormals;

    SerializedObject serializedInstance;
    SerializedProperty radiusProperty;
    SerializedProperty spawnCountProperty;
    SerializedProperty spawnPrefabProperty;
    SerializedProperty enablePaintingProperty;
    SerializedProperty singleObjectModeProperty;
    SerializedProperty useRandomRotationsProperty;
    SerializedProperty shouldEmbedProperty;
    SerializedProperty embedAmountProperty;

    void OnEnable()
    {
        serializedInstance = new SerializedObject(this);
        radiusProperty = serializedInstance.FindProperty("radius");
        spawnCountProperty = serializedInstance.FindProperty("spawnCount");
        spawnPrefabProperty = serializedInstance.FindProperty("spawnPrefab");
        enablePaintingProperty = serializedInstance.FindProperty("enablePainting");
        singleObjectModeProperty = serializedInstance.FindProperty("singleObjectMode");
        useRandomRotationsProperty = serializedInstance.FindProperty("useRandomRotations");
        shouldEmbedProperty = serializedInstance.FindProperty("shouldEmbed");
        embedAmountProperty = serializedInstance.FindProperty("embedAmount");

        GenerateRandomPoints();

        SceneView.duringSceneGui += DuringSceneGUI;
    }

    void OnDisable() => SceneView.duringSceneGui -= DuringSceneGUI;

    void DuringSceneGUI(SceneView sceneView)
    {
        Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;

        Transform camTransform = sceneView.camera.transform;

        

        Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);


        if(Physics.Raycast(ray, out RaycastHit hit))
        {
            

            if(singleObjectMode)
            {
                Handles.color = Color.yellow;
                Handles.DrawAAPolyLine(hit.point, hit.point + hit.normal);

                objectPoints = new List<Vector3>();
                objectNormals = new List<Vector3>();
                objectPoints.Add(hit.point);
                objectNormals.Add(hit.normal);
            }
            else
            {
                Vector3 hitNormal = hit.normal;
                Vector3 hitTangent = Vector3.Cross(hitNormal, camTransform.up).normalized;
                Vector3 hitBiTangent = Vector3.Cross(hitNormal, hitTangent);

                Handles.color = Color.red;
                Handles.DrawAAPolyLine(hit.point, hit.point + hit.normal);

                Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
                Handles.color = Color.green;
                Handles.DrawWireDisc(hit.point, hit.normal, radius);
                Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;

                Handles.color = Color.yellow;

                objectPoints = new List<Vector3>();
                objectNormals = new List<Vector3>();
                foreach (Vector2 point in randomPoints)
                {
                    Vector3 rayOrigin = hit.point + (hitTangent * point.x + hitBiTangent * point.y) * radius;

                    rayOrigin += hitNormal * raycastOriginDistance;

                    Vector3 rayDirection = -hitNormal;
                    Ray pointRay = new Ray(rayOrigin, rayDirection);

                    if (Physics.Raycast(pointRay, out RaycastHit pointHit))
                    {
                        objectPoints.Add(pointHit.point);
                        objectNormals.Add(pointHit.normal);
                        Handles.DrawAAPolyLine(pointHit.point, pointHit.point + pointHit.normal);
                    }


                }
            }

            
        }

        if(Event.current.type == EventType.MouseMove)
        {
            sceneView.Repaint();
        }

        bool escapeKey = (Event.current.modifiers & EventModifiers.Alt) != 0;

        if(enablePainting && !escapeKey && Event.current.type == EventType.ScrollWheel)
        {
            float scrollDirection = Event.current.delta.y;

            serializedInstance.Update();

            radiusProperty.floatValue += scrollDirection;
            
            serializedInstance.ApplyModifiedProperties();
            Repaint();

            Event.current.Use();
            
        }

        if(enablePainting && !escapeKey && Event.current.type == EventType.MouseDown && Event.current.button == 0)
        {
            SpawnObjects();
            Event.current.Use();
            Selection.activeObject = null;
        }
    }

    private void OnGUI()
    {

        serializedInstance.Update();
        EditorGUILayout.PropertyField(radiusProperty);
        EditorGUILayout.PropertyField(spawnCountProperty);
        EditorGUILayout.PropertyField(spawnPrefabProperty);
        EditorGUILayout.PropertyField(enablePaintingProperty);
        EditorGUILayout.PropertyField(singleObjectModeProperty);
        EditorGUILayout.PropertyField(useRandomRotationsProperty);
        EditorGUILayout.PropertyField(shouldEmbedProperty);

        if(shouldEmbedProperty.boolValue)
        {
            EditorGUILayout.PropertyField(embedAmountProperty);
        }

        radiusProperty.floatValue = Mathf.Max(1.0f, radiusProperty.floatValue);
        spawnCountProperty.intValue = Mathf.Max(1, spawnCountProperty.intValue);

        if(serializedInstance.ApplyModifiedProperties())
        {
            GenerateRandomPoints();
            SceneView.RepaintAll();
        }

        if(GUILayout.Button("Generate New Points"))
        {
            GenerateRandomPoints();
            SceneView.RepaintAll();
        }

        if(Event.current.type == EventType.MouseDown && Event.current.button == 0)
        {
            GUI.FocusControl(null);
            Repaint();
        }
    }

    void GenerateRandomPoints()
    {
        randomPoints = new Vector2[spawnCount];
        for(int i = 0; i < spawnCount; i++)
        {
            randomPoints[i] = Random.insideUnitCircle;
        }

    }

    void SpawnObjects()
    {
        if (spawnPrefab == null)
            return;

        GameObject parent = GameObject.Find("Painted Objects");

        if(parent == null)
        {
            parent = new GameObject();
            parent.name = "Painted Objects";
        }

        for(int i = 0; i < objectPoints.Count; i++)
        {
            Vector3 loc = objectPoints[i];
            Vector3 normal = objectNormals[i];

            GameObject obj = GameObject.Instantiate(spawnPrefab, parent.transform);
            

            if(useRandomRotations)
            {
                float randomAngle = Random.Range(0.0f, 360.0f);
                Quaternion randRotation = Quaternion.Euler(0f, 0f, randomAngle);
                Quaternion objRotation = Quaternion.LookRotation(normal) * randRotation * Quaternion.Euler(90f, 0f, 0f);

                obj.transform.rotation = objRotation;
            }

            if(shouldEmbed)
            {
                loc += -normal * embedAmount;
            }

            obj.transform.position = loc;


            Undo.RegisterCreatedObjectUndo(obj, "Paint");
        }

        GenerateRandomPoints();
    }

}
#endif