// this is the delegate we're going to return to apply a brush stamp. Your passed a paint job,
        // which contains important cached information to make painting fast, and hold the actual stream data.
        // the index is the index into the vertex array, the val is the brush data we supplied in GetBrushObject,
        // and r is a 0 to 1 with how far to tween the value towards the brush (brush pressure * deltatime)
        void LerpFunc(PaintJob j, int idx, ref object val, float r)
        {
            // retrieve our brush data and get the stream we're painting into
             BrushData bd = val as BrushData;
             var s = j.stream;
             // use our vertex position to generate noise. We use the get position function because it will
             // return a vert position from the original meshes cached verticies or modified verticies if
             // we've modified them. This makes it compatible with deformations, etc.
             Vector3 pos = j.GetPosition(idx);
             // convert into world space
             pos = j.renderer.localToWorldMatrix.MultiplyPoint(pos);
             // scale by frequency
             pos.x *= bd.frequency;
             pos.y *= bd.frequency;
             pos.z *= bd.frequency;
             float noise = 0.5f * (0.5f * JBooth.VertexPainterPro.SimplexNoise.Noise.Generate(pos.x, pos.y, pos.z) + 0.5f);
             noise += 0.25f * (0.5f * JBooth.VertexPainterPro.SimplexNoise.Noise.Generate(pos.y * 2.031f, pos.z * 2.031f, pos.x * 2.031f) + 0.5f);
             noise += 0.25f * (0.5f * JBooth.VertexPainterPro.SimplexNoise.Noise.Generate(pos.z * 4.01f, pos.x * 4.01f, pos.y * 4.01f) + 0.5f);
             noise *= bd.amplitude;
             // lerp the noise in
             Color c = s.colors[idx];
             c.r = Mathf.Lerp(c.r, noise, r);
             c.g = Mathf.Lerp(c.g, noise, r);
             c.b = Mathf.Lerp(c.b, noise, r);

             s.colors[idx] = c;
        }
        // this is the delegate we're going to return to apply a brush stamp. Your passed a paint job,
        // which contains important cached information to make painting fast, and hold the actual stream data.
        // the index is the index into the vertex array, the val is the brush data we supplied in GetBrushObject,
        // and r is a 0 to 1 with how far to tween the value towards the brush (brush pressure * deltatime)
        void LerpFunc(PaintJob j, int idx, ref object val, float r)
        {
            // retrieve our brush data and get the stream we're painting into
            BrushData bd = val as BrushData;
            var       s  = j.stream;
            // use our vertex position to generate noise. We use the get position function because it will
            // return a vert position from the original meshes cached verticies or modified verticies if
            // we've modified them. This makes it compatible with deformations, etc.
            Vector3 pos = j.GetPosition(idx);

            // convert into world space
            pos = j.renderer.localToWorldMatrix.MultiplyPoint(pos);
            // scale by frequency
            pos.x *= bd.frequency;
            pos.y *= bd.frequency;
            pos.z *= bd.frequency;
            float noise = 0.5f * (0.5f * JBooth.VertexPainterPro.SimplexNoise.Noise.Generate(pos.x, pos.y, pos.z) + 0.5f);

            noise += 0.25f * (0.5f * JBooth.VertexPainterPro.SimplexNoise.Noise.Generate(pos.y * 2.031f, pos.z * 2.031f, pos.x * 2.031f) + 0.5f);
            noise += 0.25f * (0.5f * JBooth.VertexPainterPro.SimplexNoise.Noise.Generate(pos.z * 4.01f, pos.x * 4.01f, pos.y * 4.01f) + 0.5f);
            noise *= bd.amplitude;
            // lerp the noise in
            Color c = s.colors[idx];

            c.r = Mathf.Lerp(c.r, noise, r);
            c.g = Mathf.Lerp(c.g, noise, r);
            c.b = Mathf.Lerp(c.b, noise, r);

            s.colors[idx] = c;
        }
Пример #3
0
        public void OnGUI(PaintJob[] jobs)
        {
            bakingTex = EditorGUILayout.ObjectField("Texture", bakingTex, typeof(Texture2D), false) as Texture2D;

             bakeSourceUV = (BakeSourceUV)EditorGUILayout.EnumPopup("Source UVs", bakeSourceUV);
             bakeChannel = (BakeChannel)EditorGUILayout.EnumPopup("Bake To", bakeChannel);
             if (bakeSourceUV == BakeSourceUV.WorldSpaceXY || bakeSourceUV == BakeSourceUV.WorldSpaceXZ || bakeSourceUV == BakeSourceUV.WorldSpaceYZ)
             {
            worldSpaceLower = EditorGUILayout.Vector2Field("Lower world position", worldSpaceLower);
            worldSpaceUpper = EditorGUILayout.Vector2Field("Upper world position", worldSpaceUpper);
             }
             EditorGUILayout.BeginHorizontal();
             EditorGUILayout.Space();
             if (GUILayout.Button("Bake"))
             {
            if (bakingTex != null)
            {
               BakeFromTexture(jobs);
            }
            else
            {
               EditorUtility.DisplayDialog("Error", "Baking texture is not set", "ok");
            }
             }
             EditorGUILayout.Space();
             EditorGUILayout.EndHorizontal();
        }
Пример #4
0
        void BakeColor(PaintJob job, BakeChannel bc, Vector4 val, int i)
        {
            switch (bc)
            {
            case BakeChannel.Color:
            {
                job.stream.colors[i] = new Color(val.x, val.y, val.z, val.w);
                break;
            }

            case BakeChannel.UV0:
            {
                job.stream.uv0[i] = val;
                break;
            }

            case BakeChannel.UV1:
            {
                job.stream.uv1[i] = val;
                break;
            }

            case BakeChannel.UV2:
            {
                job.stream.uv2[i] = val;
                break;
            }

            case BakeChannel.UV3:
            {
                job.stream.uv3[i] = val;
                break;
            }
            }
        }
 void BakeColor(PaintJob job, BakeChannel bc, Vector4 val, int i)
 {
    switch (bc)
    {
       case BakeChannel.Color:
       {
          job.stream.colors[i] = new Color(val.x, val.y, val.z, val.w); break;
       }
       case BakeChannel.UV0:
       {
          job.stream.uv0[i] = val;
          break;
       }
       case BakeChannel.UV1:
       {
          job.stream.uv1[i] = val;
          break;
       }
       case BakeChannel.UV2:
       {
          job.stream.uv2[i] = val;
          break;
       }
       case BakeChannel.UV3:
       {
          job.stream.uv3[i] = val;
          break;
       }
    }
 }
Пример #6
0
 public void OnGUI(PaintJob[] jobs)
 {
     EditorGUILayout.BeginHorizontal();
      if (GUILayout.Button("Combine Meshes"))
      {
     VertexPainterUtilities.MergeMeshes(jobs);
      }
      if (GUILayout.Button("Combine and Save"))
      {
     if (jobs.Length != 0)
     {
        string path = EditorUtility.SaveFilePanel("Save Asset", Application.dataPath, "models", "asset");
        if (!string.IsNullOrEmpty(path))
        {
           path = FileUtil.GetProjectRelativePath(path);
           GameObject go = VertexPainterUtilities.MergeMeshes(jobs);
           Mesh m = go.GetComponent<MeshFilter>().sharedMesh;
           AssetDatabase.CreateAsset(m, path);
           AssetDatabase.SaveAssets();
           AssetDatabase.ImportAsset(path);
           GameObject.DestroyImmediate(go);
        }
     }
      }
      EditorGUILayout.EndHorizontal();
 }
        public static GameObject MergeMeshes(PaintJob[] jobs)
        {
            if (jobs.Length == 0)
            return null;
             List<CombineInstance> meshes = new List<CombineInstance>();
             for (int i = 0; i < jobs.Length; ++i)
             {
            Mesh m = BakeDownMesh(jobs[i].meshFilter.sharedMesh, jobs[i].stream);
            CombineInstance ci = new CombineInstance();
            ci.mesh = m;
            ci.transform = jobs[i].meshFilter.transform.localToWorldMatrix;
            meshes.Add(ci);
             }

             Mesh mesh = new Mesh();
             mesh.CombineMeshes(meshes.ToArray());
             GameObject go = new GameObject("Combined Mesh");
             go.AddComponent<MeshRenderer>();
             var mf = go.AddComponent<MeshFilter>();
             mesh.Optimize();
             mesh.RecalculateBounds();
             mesh.UploadMeshData(false);
             mf.sharedMesh = mesh;
             for (int i = 0; i < meshes.Count; ++i)
             {
            GameObject.DestroyImmediate(meshes[i].mesh);
             }
             return go;
        }
Пример #8
0
        void LerpFunc(PaintJob j, int idx, ref object val, float r)
        {
            if (didHit && j.stream.gameObject != target && target != null)
            {
                // convert from world space to local space
                Vector3 norm = j.stream.GetSafeNormal(idx);
                Vector4 tang = j.stream.GetSafeTangent(idx);

                var mtx = j.stream.transform.worldToLocalMatrix;
                var t   = tangent;
                t   = mtx.MultiplyVector(tangent);
                t.w = tangent.w;

                j.stream.normals[idx]  = Vector3.Lerp(norm, mtx.MultiplyVector(norm), r);
                j.stream.tangents[idx] = Vector4.Lerp(tang, t, r);
            }
            if (didHit && terrainTarget != null)
            {
                // retrieve our brush data and get the stream we're painting into

                Vector3 n = j.stream.normals[idx];
                Vector4 t = j.stream.tangents[idx];

                Vector3 pos = j.GetPosition(idx);

                Vector3 iNormal = terrainTarget.terrainData.GetInterpolatedNormal(pos.x, pos.z);
                iNormal = j.stream.transform.InverseTransformDirection(iNormal);

                j.stream.normals[idx] = Vector3.Lerp(n, iNormal, r);
                Vector3 tangentXYZ = Vector3.Cross(j.stream.normals[idx], new Vector3(0, 0, 1));
                Vector4 tangent    = new Vector4(tangentXYZ.x, tangentXYZ.y, tangentXYZ.z, -1);

                j.stream.tangents[idx] = Vector4.Lerp(t, tangent, r);
            }
        }
Пример #9
0
 void LerpFunc(PaintJob j, int idx, ref object val, float r)
 {
     if (didHit)
     {
         j.stream.normals[idx]  = Vector3.Lerp(j.stream.normals[idx], normal, r);
         j.stream.tangents[idx] = Vector4.Lerp(j.stream.tangents[idx], tangent, r);
     }
 }
 static void FlowColorRG(PaintJob j, int idx, ref object v, float r)
 {
    Vector2 vv = (Vector2)v;
    var s = j.stream;
    Color c = s.colors[idx];
    s.colors[idx].r = Mathf.Lerp(c.r, vv.x, r);
    s.colors[idx].g = Mathf.Lerp(c.g, vv.y, r); 
 }
 static void FlowColorBA(PaintJob j, int idx, ref object v, float r)
 {
    Vector2 vv = (Vector2)v;
    var s = j.stream;
    Color c = s.colors[idx];
    s.colors[idx].b = Mathf.Lerp(c.b, vv.x, r);
    s.colors[idx].a = Mathf.Lerp(c.a, vv.y, r); 
 }
 static void FlowUV1_XY(PaintJob j, int idx, ref object v, float r)
 {
    var s = j.stream;
    Vector4 o = s.uv1[idx];
    Vector2 t = (Vector2)v;
    o.x = Mathf.Lerp(o.x, t.x, r);
    o.y = Mathf.Lerp(o.y, t.y, r);
    s.uv1[idx] = o;
 }
 static void FlowUV2_ZW(PaintJob j, int idx, ref object v, float r)
 {
    var s = j.stream;
    Vector4 o = s.uv2[idx];
    Vector2 t = (Vector2)v;
    o.z = Mathf.Lerp(o.z, t.x, r);
    o.w = Mathf.Lerp(o.w, t.y, r);
    s.uv2[idx] = o;
 }
Пример #14
0
 void DoBakePivot(PaintJob[] jobs)
 {
     switch (pivotTarget)
      {
     case PivotTarget.UV0:
        {
           InitBakeChannel(BakeChannel.UV0, jobs);
           foreach (PaintJob job in jobs)
           {
              Vector3 lp = bakePivotUseLocal ? job.meshFilter.transform.localPosition : job.meshFilter.transform.position;
              job.stream.SetUV0(new Vector4(lp.x, lp.y, lp.z, UnityEngine.Random.Range(0.0f, 1.0f)), job.verts.Length);
              EditorUtility.SetDirty(job.stream);
              EditorUtility.SetDirty(job.stream.gameObject);
           }
           break;
        }
     case PivotTarget.UV1:
        {
           InitBakeChannel(BakeChannel.UV1, jobs);
           foreach (PaintJob job in jobs)
           {
              Vector3 lp = bakePivotUseLocal ? job.meshFilter.transform.localPosition : job.meshFilter.transform.position;
              job.stream.SetUV1(new Vector4(lp.x, lp.y, lp.z, UnityEngine.Random.Range(0.0f, 1.0f)), job.verts.Length);
              EditorUtility.SetDirty(job.stream);
              EditorUtility.SetDirty(job.stream.gameObject);
           }
           break;
        }
     case PivotTarget.UV2:
        {
           InitBakeChannel(BakeChannel.UV2, jobs);
           foreach (PaintJob job in jobs)
           {
              Vector3 lp = bakePivotUseLocal ? job.meshFilter.transform.localPosition : job.meshFilter.transform.position;
              job.stream.SetUV2(new Vector4(lp.x, lp.y, lp.z, UnityEngine.Random.Range(0.0f, 1.0f)), job.verts.Length);
              EditorUtility.SetDirty(job.stream);
              EditorUtility.SetDirty(job.stream.gameObject);
           }
           break;
        }
     case PivotTarget.UV3:
        {
           InitBakeChannel(BakeChannel.UV3, jobs);
           foreach (PaintJob job in jobs)
           {
              Vector3 lp = bakePivotUseLocal ? job.meshFilter.transform.localPosition : job.meshFilter.transform.position;
              job.stream.SetUV3(new Vector4(lp.x, lp.y, lp.z, UnityEngine.Random.Range(0.0f, 1.0f)), job.verts.Length);
              EditorUtility.SetDirty(job.stream);
              EditorUtility.SetDirty(job.stream.gameObject);
           }
           break;
        }
      }
 }
Пример #15
0
        public void OnGUI(PaintJob[] jobs)
        {
            EditorGUILayout.BeginHorizontal();
             EditorGUILayout.Space();
             if (GUILayout.Button("Save Mesh"))
             {
            VertexPainterUtilities.SaveMesh(jobs);
             }

             EditorGUILayout.Space();
             EditorGUILayout.EndHorizontal();
        }
 public void OnGUI(PaintJob[] jobs)
 {
     EditorGUILayout.HelpBox("Multiply a channel by a value. Useful for scaling UVs, objects, etc", MessageType.Info);
      bakeChannel = (BakeChannel)EditorGUILayout.EnumPopup("Channel", bakeChannel);
      multValue = EditorGUILayout.FloatField("Multiply By", multValue);
      EditorGUILayout.BeginHorizontal();
      EditorGUILayout.Space();
      if (GUILayout.Button("Multiply It"))
      {
     Bake(jobs);
      }
      EditorGUILayout.Space();
      EditorGUILayout.EndHorizontal();
 }
Пример #17
0
        public void OnGUI(PaintJob[] jobs)
        {
            pivotTarget = (PivotTarget)EditorGUILayout.EnumPopup("Store in", pivotTarget);
             bakePivotUseLocal = EditorGUILayout.Toggle("Use Local Space", bakePivotUseLocal);

             EditorGUILayout.BeginHorizontal();
             EditorGUILayout.Space();
             if (GUILayout.Button("Bake Pivot"))
             {
            DoBakePivot(jobs);
             }
             if (GUILayout.Button("Bake Rotation"))
             {
            DoBakeRotation(jobs);
             }

             EditorGUILayout.Space();
             EditorGUILayout.EndHorizontal();
        }
        public static void SaveMesh(PaintJob[] jobs)
        {
            if (jobs.Length != 0)
             {
            string path = EditorUtility.SaveFilePanel("Save Asset", Application.dataPath, "models", "asset");
            if (!string.IsNullOrEmpty(path))
            {
               path = FileUtil.GetProjectRelativePath(path);
               Mesh firstMesh = BakeDownMesh(jobs[0].meshFilter.sharedMesh, jobs[0].stream);

               AssetDatabase.CreateAsset(firstMesh, path);

               for (int i = 1; i < jobs.Length; ++i)
               {
                  Mesh m = BakeDownMesh(jobs[i].meshFilter.sharedMesh, jobs[i].stream);
                  AssetDatabase.AddObjectToAsset(m, firstMesh);
               }
               AssetDatabase.SaveAssets();
               AssetDatabase.ImportAsset(path);
            }
             }
        }
Пример #19
0
        public void OnGUI(PaintJob[] jobs)
        {
            var window = VertexPainterWindow.GetWindow<VertexPainterWindow>();
             window.brushMode = (VertexPainterWindow.BrushTarget)EditorGUILayout.EnumPopup("Target Channel", window.brushMode);
             aoSamples = EditorGUILayout.IntSlider("Samples", aoSamples, 64, 1024);
             EditorGUILayout.BeginHorizontal();
             aoRange = EditorGUILayout.Vector2Field("Range (Min, Max)", aoRange);
             aoRange.x = Mathf.Max(aoRange.x, 0.0001f);
             EditorGUILayout.EndHorizontal();
             aoIntensity = EditorGUILayout.Slider("Intensity", aoIntensity, 0.25f, 4.0f);
             bakeLighting = EditorGUILayout.Toggle("Bake Lighting", bakeLighting);
             if (bakeLighting)
             {
            aoLightAmbient = EditorGUILayout.ColorField("Light Ambient", aoLightAmbient);
             }
             aoBakeMode = (AOBakeMode)EditorGUILayout.EnumPopup("Mode", aoBakeMode);

             EditorGUILayout.Space();
             if (GUILayout.Button("Bake"))
             {
            DoBakeAO(jobs, window);
             }
        }
Пример #20
0
        void BakeAO()
        {
            Light[] aoLights = null;
            if (bakeLighting)
            {
                aoLights = GameObject.FindObjectsOfType <Light>();
            }
            int sample   = 0;
            int numVerts = 0;

            for (int i = 0; i < jobs.Length; ++i)
            {
                numVerts += jobs[i].verts.Length;
            }
            int numSamples = numVerts * aoSamples;

            float oldFloat = floatBrushValue;
            Color oldColor = brushColor;
            int   oldVal   = brushValue;

            // add temp colliders if needed
            bool[] tempCollider = new bool[jobs.Length];
            for (int jIdx = 0; jIdx < jobs.Length; ++jIdx)
            {
                PaintJob job = jobs[jIdx];

                if (job.meshFilter.GetComponent <Collider>() == null)
                {
                    job.meshFilter.gameObject.AddComponent <MeshCollider>();
                    tempCollider[jIdx] = true;
                }
            }

            // do AO
            for (int jIdx = 0; jIdx < jobs.Length; ++jIdx)
            {
                PaintJob job = jobs[jIdx];

                PrepBrushMode(job);
                // bake down the mesh so we take instance positions into account..
                Mesh      mesh  = BakeDownMesh(job.meshFilter.sharedMesh, job.stream);
                Vector3[] verts = mesh.vertices;
                if (mesh.normals == null || mesh.normals.Length == 0)
                {
                    mesh.RecalculateNormals();
                }
                Vector3[] normals = mesh.normals;

                brushValue      = 255;
                floatBrushValue = 1.0f;
                brushColor      = Color.white;
                var val    = GetBrushValue();
                var setter = GetSetter(job.stream);
                for (int i = 0; i < job.verts.Length; ++i)
                {
                    setter.Invoke(i, ref val);
                }


                for (int i = 0; i < verts.Length; i++)
                {
                    Vector3 norm = normals[i];
                    // to world space!
                    Vector3 v = job.meshFilter.transform.TransformPoint(verts[i]);
                    Vector3 n = job.meshFilter.transform.TransformPoint(verts[i] + norm);
                    Vector3 worldSpaceNormal = (n - v).normalized;

                    float totalOcclusion = 0;

                    // the slow part..
                    for (int j = 0; j < aoSamples; j++)
                    {
                        // random rotate around hemisphere
                        float rot  = 180.0f;
                        float rot2 = rot / 2.0f;
                        float rotx = ((rot * Random.value) - rot2);
                        float roty = ((rot * Random.value) - rot2);
                        float rotz = ((rot * Random.value) - rot2);

                        Vector3    dir    = Quaternion.Euler(rotx, roty, rotz) * Vector3.up;
                        Quaternion dirq   = Quaternion.FromToRotation(Vector3.up, worldSpaceNormal);
                        Vector3    ray    = dirq * dir;
                        Vector3    offset = Vector3.Reflect(ray, worldSpaceNormal);

                        // raycast
                        ray = ray * (aoRange.y / ray.magnitude);
                        if (Physics.Linecast(v - (offset * 0.1f), v + ray, out hit))
                        {
                            if (hit.distance > aoRange.x)
                            {
                                totalOcclusion += Mathf.Clamp01(1 - (hit.distance / aoRange.y));
                            }
                        }

                        sample++;
                        if (sample % 500 == 0)
                        {
                            EditorUtility.DisplayProgressBar("Baking AO...", "Baking...", (float)sample / (float)numSamples);
                        }
                    }

                    totalOcclusion = Mathf.Clamp01(1 - ((totalOcclusion * aoIntensity) / aoSamples));

                    if (aoLights != null && aoLights.Length > 0)
                    {
                        Color c = aoLightAmbient;
                        for (int l = 0; l < aoLights.Length; ++l)
                        {
                            Light light = aoLights[l];
                            ApplyAOLight(ref c, light, v, n);
                        }
                        c.r       *= totalOcclusion;
                        c.g       *= totalOcclusion;
                        c.b       *= totalOcclusion;
                        c.a        = totalOcclusion;
                        brushColor = c;

                        // if we're lit and targeting a channel other than color, bake max intensity..
                        floatBrushValue = Mathf.Max(Mathf.Max(c.r, c.g), c.b) * totalOcclusion;
                        brushValue      = (int)(floatBrushValue * 255);
                    }
                    else
                    {
                        brushColor.r = totalOcclusion;
                        brushColor.g = totalOcclusion;
                        brushColor.b = totalOcclusion;
                        brushColor.a = totalOcclusion;

                        floatBrushValue = totalOcclusion;
                        brushValue      = (int)(totalOcclusion * 255);
                    }
                    val = GetBrushValue();
                    setter.Invoke(i, ref val);
                }
                job.stream.Apply();
                DestroyImmediate(mesh);

                brushValue      = oldVal;
                floatBrushValue = oldFloat;
                brushColor      = oldColor;
            }
            // remove temp colliders
            for (int jIdx = 0; jIdx < jobs.Length; ++jIdx)
            {
                if (tempCollider[jIdx] == true)
                {
                    Collider c = jobs[jIdx].meshFilter.GetComponent <Collider>();
                    if (c != null)
                    {
                        DestroyImmediate(c);
                    }
                }
            }

            EditorUtility.ClearProgressBar();
            SceneView.RepaintAll();
        }
Пример #21
0
        void DoBakeAO(PaintJob[] jobs, VertexPainterWindow window)
        {
            Light[] aoLights = null;
             if (bakeLighting)
             {
            aoLights = GameObject.FindObjectsOfType<Light>();
             }
             int sample = 0;
             int numVerts = 0;
             for (int i = 0; i < jobs.Length; ++i)
             {
            numVerts += jobs[i].verts.Length;
             }
             int numSamples = numVerts * aoSamples;

             float oldFloat = window.floatBrushValue;
             Color oldColor = window.brushColor;
             int oldVal = window.brushValue;

             // add temp colliders if needed
             bool[] tempCollider = new bool[jobs.Length];
             for (int jIdx = 0; jIdx < jobs.Length; ++jIdx)
             {
            PaintJob job = jobs[jIdx];

            if (job.meshFilter.GetComponent<Collider>() == null)
            {
               job.meshFilter.gameObject.AddComponent<MeshCollider>();
               tempCollider[jIdx] = true;
            }
             }

             // do AO
             for (int jIdx = 0; jIdx < jobs.Length; ++jIdx)
             {
            PaintJob job = jobs[jIdx];

            window.PrepBrushMode(job);
            // bake down the mesh so we take instance positions into account..
            Mesh mesh = VertexPainterUtilities.BakeDownMesh(job.meshFilter.sharedMesh, job.stream);
            Vector3[] verts = mesh.vertices;
            if (mesh.normals == null || mesh.normals.Length == 0)
            {
               mesh.RecalculateNormals();
            }
            Vector3[] normals = mesh.normals;

            window.brushValue = 255;
            window.floatBrushValue = 1.0f;
            window.brushColor = Color.white;
            var val = window.GetBrushValue();
            VertexPainterWindow.Lerper lerper = null;
            VertexPainterWindow.Multiplier mult = null;

            if (aoBakeMode == AOBakeMode.Replace)
            {
               lerper = window.GetLerper();
               for (int i = 0; i < job.verts.Length; ++i)
               {
                  lerper.Invoke(job, i, ref val, 1);
               }
            }
            else
            {
               mult = window.GetMultiplier();
            }

            for (int i = 0; i<verts.Length; i++)
            {
               Vector3 norm = normals[i];
               // to world space!
               Vector3 v = job.meshFilter.transform.TransformPoint(verts[i]);
               Vector3 n = job.meshFilter.transform.TransformPoint(verts[i] + norm);
               Vector3 worldSpaceNormal = (n-v).normalized;

               float totalOcclusion = 0;

               // the slow part..
               for (int j = 0; j < aoSamples; j++)
               {
                  // random rotate around hemisphere
                  float rot = 180.0f;
                  float rot2 = rot / 2.0f;
                  float rotx = (( rot * Random.value ) - rot2);
                  float roty = (( rot * Random.value ) - rot2);
                  float rotz = (( rot * Random.value ) - rot2);

                  Vector3 dir = Quaternion.Euler( rotx, roty, rotz ) * Vector3.up;
                  Quaternion dirq = Quaternion.FromToRotation(Vector3.up, worldSpaceNormal);
                  Vector3 ray = dirq * dir;
                  Vector3 offset = Vector3.Reflect( ray, worldSpaceNormal );

                  // raycast
                  ray = ray * (aoRange.y/ray.magnitude);
                  if ( Physics.Linecast( v-(offset*0.1f), v + ray, out hit ) )
                  {
                     if ( hit.distance > aoRange.x )
                     {
                        totalOcclusion += Mathf.Clamp01( 1 - ( hit.distance / aoRange.y ) );
                     }
                  }

                  sample++;
                  if (sample % 500 == 0)
                  {
                     EditorUtility.DisplayProgressBar("Baking AO...", "Baking...", (float)sample / (float)numSamples);
                  }
               }

               totalOcclusion = Mathf.Clamp01( 1 - ((totalOcclusion*aoIntensity)/aoSamples) );

               if (aoLights != null && aoLights.Length > 0)
               {
                  Color c = aoLightAmbient;
                  for (int l = 0; l < aoLights.Length; ++l)
                  {
                     Light light = aoLights[l];
                     ApplyAOLight(ref c, light, v, n);
                  }
                  c.r *= totalOcclusion;
                  c.g *= totalOcclusion;
                  c.b *= totalOcclusion;
                  c.a = totalOcclusion;
                  window.brushColor = c;

                  // if we're lit and targeting a channel other than color, bake max intensity..
                  window.floatBrushValue = Mathf.Max(Mathf.Max(c.r, c.g), c.b) * totalOcclusion;
                  window.brushValue = (int)(window.floatBrushValue * 255);
               }
               else
               {
                  window.brushColor.r = totalOcclusion;
                  window.brushColor.g = totalOcclusion;
                  window.brushColor.b = totalOcclusion;
                  window.brushColor.a = totalOcclusion;

                  window.floatBrushValue = totalOcclusion;
                  window.brushValue = (int)(totalOcclusion * 255);
               }
               val = window.GetBrushValue();
               if (aoBakeMode == AOBakeMode.Replace)
               {
                  lerper.Invoke(job, i, ref val, 1);
               }
               else
               {
                  mult.Invoke(job.stream, i, ref val);
               }
            }
            job.stream.Apply();
            EditorUtility.SetDirty(job.stream);
            EditorUtility.SetDirty(job.stream.gameObject);
            GameObject.DestroyImmediate(mesh);

            window.brushValue = oldVal;
            window.floatBrushValue = oldFloat;
            window.brushColor = oldColor;
             }
             // remove temp colliders
             for (int jIdx = 0; jIdx < jobs.Length; ++jIdx)
             {
            if (tempCollider[jIdx] == true)
            {
               Collider c = jobs[jIdx].meshFilter.GetComponent<Collider>();
               if (c != null)
               {
                  GameObject.DestroyImmediate(c);
               }
            }
             }

             EditorUtility.ClearProgressBar();
             SceneView.RepaintAll();
        }
 void InitUV0(PaintJob j)
 {
    List<Vector4> uvs = j.stream.uv0;
    if (uvs == null || uvs.Count != j.verts.Length)
    {
       if (j.meshFilter.sharedMesh.uv != null && j.meshFilter.sharedMesh.uv.Length == j.verts.Length)
       {
          List<Vector4> nuv = new List<Vector4>(j.meshFilter.sharedMesh.vertices.Length);
          j.meshFilter.sharedMesh.GetUVs(0, nuv);
          j.stream.uv0 = nuv;
       }
       else
       {
          j.stream.SetUV0(Vector4.zero, j.verts.Length);
       }
    }
 }
 static void ColorRGBAOverlay(PaintJob j, int idx, ref object v, float r)
 {
    var st = j.stream;
    Color c0 = st.colors[idx];
    Color t = (Color)v;
    c0.r = Mathf.Lerp(c0.r, c0.r < 0.5f ? (2.0f * c0.r * t.r) : (1.0f - 2.0f * (1.0f - c0.r) * (1.0f - t.r)), r);
    c0.g = Mathf.Lerp(c0.g, c0.g < 0.5f ? (2.0f * c0.g * t.g) : (1.0f - 2.0f * (1.0f - c0.g) * (1.0f - t.g)), r);
    c0.b = Mathf.Lerp(c0.b, c0.b < 0.5f ? (2.0f * c0.b * t.b) : (1.0f - 2.0f * (1.0f - c0.b) * (1.0f - t.b)), r);
    st.colors[idx] = c0;
 }
 void InitUV3(PaintJob j)
 {
    var uvs = j.stream.uv3;
    if (uvs == null || uvs.Count != j.verts.Length)
    {
       if (j.meshFilter.sharedMesh.uv4 != null && j.meshFilter.sharedMesh.uv4.Length == j.verts.Length)
       {
          List<Vector4> nuv = new List<Vector4>(j.meshFilter.sharedMesh.vertices.Length);
          j.meshFilter.sharedMesh.GetUVs(3, nuv);
          j.stream.uv3 = nuv;
       }
       else
       {
          j.stream.SetUV3(Vector2.zero, j.verts.Length);
       }
    }
 }
 void InitNormalTangent(PaintJob j)
 {
    Vector3[] norms = j.stream.normals;
    if (norms == null || norms.Length != j.verts.Length)
    {
       int vc = j.meshFilter.sharedMesh.vertexCount;
       if (j.stream.normals == null || j.stream.normals.Length != vc)
       {
          j.stream.normals = new Vector3[j.meshFilter.sharedMesh.vertices.Length];
          j.meshFilter.sharedMesh.normals.CopyTo(j.stream.normals, 0);
       }
       if (j.stream.tangents == null || j.stream.tangents.Length != vc)
       {
          j.stream.tangents = new Vector4[j.meshFilter.sharedMesh.vertices.Length];
          j.meshFilter.sharedMesh.tangents.CopyTo(j.stream.tangents, 0);
       }
    }
    return;
 }
 void InitPositions(PaintJob j)
 {
    Vector3[] pos = j.stream.positions;
    if (pos == null || pos.Length != j.verts.Length)
    {
       int vc = j.meshFilter.sharedMesh.vertexCount;
       if (j.stream.positions == null || j.stream.positions.Length != vc)
       {
          j.stream.positions = new Vector3[j.meshFilter.sharedMesh.vertices.Length];
          j.meshFilter.sharedMesh.vertices.CopyTo(j.stream.positions, 0);
       }
    }
    return;
 }
      void DrawVertexPoints(PaintJob j, Vector3 point)
      {
         Profiler.BeginSample("Draw Vertex Points");
         PrepBrushMode(j);
         // convert point into local space, so we don't have to convert every point
         point = j.renderer.transform.worldToLocalMatrix.MultiplyPoint3x4(point);
         // for some reason this doesn't handle scale, seems like it should
         // we handle it poorly until I can find a better solution
         float scale = 1.0f / Mathf.Abs(j.renderer.transform.lossyScale.x);

         float bz = scale * brushSize;

         for (int i = 0; i < j.verts.Length; ++i)
         {
            float d = Vector3.Distance(point, j.verts[i]);
            if (d < bz)
            {
               Handles.color = Color.white;
               Vector3 wp = j.meshFilter.transform.localToWorldMatrix.MultiplyPoint(j.verts[i]);
               Handles.SphereCap(0, wp, Quaternion.identity, HandleUtility.GetHandleSize(wp) * 0.02f);
            }
         }
         Profiler.EndSample();
      }
      void PrepBrushMode(PaintJob j)
      {
         if (tab == Tab.Custom)
         {
            if (customBrush == null)
            {
               Debug.Log("Custom Brush not set");
               return;
            }
            var channels = customBrush.GetChannels();
            if ((channels & VertexPainterCustomBrush.Channels.Colors) != 0)
            {
               InitColors(j);
            }
            if ((channels & VertexPainterCustomBrush.Channels.UV0) != 0)
            {
               InitUV0(j);
            }
            if ((channels & VertexPainterCustomBrush.Channels.UV1) != 0)
            {
               InitUV1(j);
            }
            if ((channels & VertexPainterCustomBrush.Channels.UV2) != 0)
            {
               InitUV2(j);
            }
            if ((channels & VertexPainterCustomBrush.Channels.UV3) != 0)
            {
               InitUV3(j);
            }
            if ((channels & VertexPainterCustomBrush.Channels.Positions) != 0)
            {
               InitPositions(j);
            }
            if ((channels & VertexPainterCustomBrush.Channels.Normals) != 0)
            {
               InitNormalTangent(j);
            }
         }
         else if (tab == Tab.Deform)
         {
            InitPositions(j);
            InitNormalTangent(j);
            return;
         }
         if (tab == Tab.Flow)
         {
            switch (flowTarget)
            {
               case FlowTarget.ColorRG:
                  goto case FlowTarget.ColorBA;
               case FlowTarget.ColorBA:
                  {
                     InitColors(j);
                     break;
                  }
               case FlowTarget.UV0_XY:
                  {
                     InitUV0(j);
                     break;
                  }
               case FlowTarget.UV1_XY:
                  {
                     InitUV1(j);
                     break;
                  }
               case FlowTarget.UV2_XY:
                  {
                     InitUV2(j);
                     break;
                  }
               case FlowTarget.UV3_XY:
                  {
                     InitUV3(j);
                     break;
                  }
            }
            return;
         }

         // make sure the instance data is initialized
         switch (brushMode)
         {
            case BrushTarget.Color:
               goto case BrushTarget.ValueA;
            case BrushTarget.ValueR:
               goto case BrushTarget.ValueA;
            case BrushTarget.ValueG:
               goto case BrushTarget.ValueA;
            case BrushTarget.ValueB:
               goto case BrushTarget.ValueA;
            case BrushTarget.ValueA:
               {
                  InitColors(j);
                  break;
               }
            case BrushTarget.UV0_X:
               goto case BrushTarget.UV0_W;
            case BrushTarget.UV0_Y:
               goto case BrushTarget.UV0_W;
            case BrushTarget.UV0_Z:
               goto case BrushTarget.UV0_W;
            case BrushTarget.UV0_W:
               {
                  InitUV0(j);
                  break;
               }
            case BrushTarget.UV1_X:
               goto case BrushTarget.UV1_W;
            case BrushTarget.UV1_Y:
               goto case BrushTarget.UV1_W;
            case BrushTarget.UV1_Z:
               goto case BrushTarget.UV1_W;
            case BrushTarget.UV1_W:
               {
                  InitUV1(j);
                  break;
               }
            case BrushTarget.UV2_X:
               goto case BrushTarget.UV2_W;
            case BrushTarget.UV2_Y:
               goto case BrushTarget.UV2_W;
            case BrushTarget.UV2_Z:
               goto case BrushTarget.UV2_W;
            case BrushTarget.UV2_W:
               {
                  InitUV2(j);
                  break;
               }
            case BrushTarget.UV3_X:
               goto case BrushTarget.UV3_W;
            case BrushTarget.UV3_Y:
               goto case BrushTarget.UV3_W;
            case BrushTarget.UV3_Z:
               goto case BrushTarget.UV3_W;
            case BrushTarget.UV3_W:
               {
                  InitUV3(j);
                  break;
               }
            case BrushTarget.UV0_AsColor:
               {
                  InitUV0(j);
                  break;
               }
            case BrushTarget.UV1_AsColor:
               {
                  InitUV1(j);
                  break;
               }
            case BrushTarget.UV2_AsColor:
               {
                  InitUV2(j);
                  break;
               }
            case BrushTarget.UV3_AsColor:
               {
                  InitUV3(j);
                  break;
               }

         }

      }
 static void UV3_AsColorRGBAOverlay(PaintJob j, int idx, ref object v, float r)
 {
    var st = j.stream;
    Vector4 vec = st.uv3[idx];
    Color c0 = new Color(vec.x, vec.y, vec.z);
    Color t = (Color)v;
    c0.r = Mathf.Lerp(c0.r, c0.r < 0.5f ? (2.0f * c0.r * t.r) : (1.0f - 2.0f * (1.0f - c0.r) * (1.0f - t.r)), r);
    c0.g = Mathf.Lerp(c0.g, c0.g < 0.5f ? (2.0f * c0.g * t.g) : (1.0f - 2.0f * (1.0f - c0.g) * (1.0f - t.g)), r);
    c0.b = Mathf.Lerp(c0.b, c0.b < 0.5f ? (2.0f * c0.b * t.b) : (1.0f - 2.0f * (1.0f - c0.b) * (1.0f - t.b)), r);
    st.uv3[idx] = new Vector4(c0.r, c0.g, c0.b, vec.w);
 }
 static void UV3_AsColorRGBADarken(PaintJob j, int idx, ref object v, float r)
 {
    var st = j.stream;
    float h, s, b;
    Vector4 vec = st.uv3[idx];
    Color c = new Color(vec.x, vec.y, vec.z);
    Color.RGBToHSV(c, out h, out s, out b);
    b = Mathf.Lerp(b, 0.0f, r);
    c = Color.HSVToRGB(h, s, b);
    st.uv3[idx] = new Vector4(c.r, c.g, c.b, vec.w);
 }
 static void ColorA(PaintJob j, int idx, ref object v, float r)
 {
    var s = j.stream;
    s.colors[idx].a = Mathf.Lerp(s.colors[idx].a, (float)v, r);
 }
      void PaintMesh(PaintJob j, Vector3 point, Lerper lerper, object value)
      {
         bool affected = false;
         PrepBrushMode(j);
         // convert point into local space, so we don't have to convert every point
         point = j.renderer.transform.worldToLocalMatrix.MultiplyPoint3x4(point);
         // for some reason this doesn't handle scale, seems like it should
         // we handle it poorly until I can find a better solution
         float scale = 1.0f / Mathf.Abs(j.renderer.transform.lossyScale.x);

         float bz = scale * brushSize;

         float pressure = Event.current.pressure > 0 ? Event.current.pressure : 1.0f;

         bool modPos = !(j.stream.positions == null || j.stream.positions.Length == 0);
         if (tab == Tab.Flow)
         {
            float strength = strokeDir.magnitude;
            Vector3 sd = strokeDir.normalized;
            Vector2 target = new Vector2(0.5f, 0.5f);
            for (int i = 0; i < j.verts.Length; ++i)
            {
               float d = Vector3.Distance(point, modPos ? j.stream.positions[i] : j.verts[i]);
               if (d < bz)
               {
                  Vector3 n = j.normals[i];
                  Vector4 t = j.tangents[i];
                  
                  if (j.stream.normals != null && j.stream.normals.Length == j.verts.Length)
                  {
                     n = j.stream.normals[i];
                  }
                  if (j.stream.tangents != null && j.stream.tangents.Length == j.verts.Length)
                  {
                     t = j.stream.tangents[i];
                  }

                  var mtx = j.meshFilter.transform.localToWorldMatrix;
                  n = mtx.MultiplyVector(n);
                  Vector3 tg = new Vector3(t.x, t.y, t.z);
                  tg = mtx.MultiplyVector(tg);
                  t.x = tg.x;
                  t.y = tg.y;
                  t.z = tg.z;

                  target.x = 0.5f;
                  target.y = 0.5f;
                  if (flowBrushType == FlowBrushType.Direction)
                  {
                     Vector3 b = Vector3.Cross(n, new Vector3(t.x, t.y, t.z) * t.w);


                     float dx = Vector3.Dot(t, sd);
                     float dy = Vector3.Dot(b, sd);
                     
                     target = new Vector2(dx, dy);
                     target.Normalize();

                     if (flowTarget == FlowTarget.ColorBA || flowTarget == FlowTarget.ColorRG || flowRemap01)
                     {
                        target.x = target.x * 0.5f + 0.5f;
                        target.y = target.y * 0.5f + 0.5f;
                     }
                  }

                  float str = 1.0f - d / bz;
                  str *= strength;  // take brush speed into account..
                  str = Mathf.Pow(str, brushFalloff);

                  object obj = target;
                  float finalStr = str * (float)deltaTime * brushFlow * pressure;
                  if (finalStr > 0)
                  {
                     affected = true;
                     lerper.Invoke(j, i, ref obj, finalStr);
                  }
               }
            }
         } 
         else if (tab == Tab.Deform)
         {
            for (int i = 0; i < j.verts.Length; ++i)
            {
               float d = Vector3.Distance(point, j.verts[i]);
               if (d < bz)
               {
                  float str = 1.0f - d / bz;
                  str = Mathf.Pow(str, brushFalloff);
                  affected = true;
                  PaintVertPosition(j, i,  str * (float)deltaTime * brushFlow * pressure);
               }
            }
         }
         else
         {
            for (int i = 0; i < j.verts.Length; ++i)
            {
               float d = Vector3.Distance(point, j.verts[i]);
               if (d < bz)
               {
                  float str = 1.0f - d / bz;
                  str = Mathf.Pow(str, brushFalloff);
                  float finalStr = str * (float)deltaTime * brushFlow * pressure;
                  if (finalStr > 0)
                  {
                     affected = true;
                     lerper.Invoke(j, i, ref value, finalStr);
                  }
               }
            }
         }
         if (affected)
         {
            j.stream.Apply();
         }
      }
      void PaintVertPosition(PaintJob j, int i, float strength)
      {
         switch (vertexMode)
         {
            case VertexMode.Adjust:
               {
                  switch (vertexContraint)
                  {
                     case VertexContraint.Normal:
                        {
                           Vector3 cur = j.stream.positions[i];
                           Vector3 dir = j.stream.normals[i].normalized;
                           dir *= strength;
                           cur += pull ? dir : -dir;
                           j.stream.positions[i] = cur;
                           break;
                        }
                     case VertexContraint.Camera:
                        {
                           Vector3 cur = j.stream.positions[i];
                           Vector3 dir = strokeDir;
                           dir *= strength;
                           cur += pull ? dir : -dir;
                           j.stream.positions[i] = cur;
                           break;
                        }
                     case VertexContraint.X:
                        {
                           Vector3 cur = j.stream.positions[i];
                           Vector3 dir = new Vector3(1, 0, 0);
                           dir *= strength;
                           cur += pull ? dir : -dir;
                           j.stream.positions[i] = cur;
                           break;
                        }
                     case VertexContraint.Y:
                        {
                           Vector3 cur = j.stream.positions[i];
                           Vector3 dir = new Vector3(0, 1, 0);
                           dir *= strength;
                           cur += pull ? dir : -dir;
                           j.stream.positions[i] = cur;
                           break;
                        }
                     case VertexContraint.Z:
                        {
                           Vector3 cur = j.stream.positions[i];
                           Vector3 dir = new Vector3(0, 0, 1);
                           dir *= strength;
                           cur += pull ? dir : -dir;
                           j.stream.positions[i] = cur;
                           break;
                        }
                  }
                  break;
               }
            case VertexMode.Smooth:
               {
                  Vector3 cur = j.stream.positions[i];
                  var con = j.vertexConnections[i];
                  for (int x = 0; x < con.Count; ++x)
                  {
                     cur += j.stream.positions[con[x]];
                  }
                  cur /= (con.Count + 1);
                  ConstrainAxis(ref cur, j.stream.positions[i]);

                  j.stream.positions[i] = Vector3.Lerp(j.stream.positions[i], cur, Mathf.Clamp01(strength));
                  break;
               }
            case VertexMode.Smear:
               {
                  Vector3 cur = j.stream.positions[i];
                  Vector3 dir = strokeDir;
                  dir *= strength;
                  cur += pull ? dir : -dir;
                  j.stream.positions[i] = cur;
                  break;
               }
            case VertexMode.HistoryEraser:
               {
                  Vector3 cur = j.stream.positions[i];
                  Vector3 orig = j.verts[i];
                  ConstrainAxis(ref orig, cur);
                  j.stream.positions[i] = Vector3.Lerp(cur, orig, Mathf.Clamp01(strength));
                  break;
               }
         }
      }
 static void UV3_W(PaintJob j, int idx, ref object v, float r)
 {
    var s = j.stream;
    Vector4 vec = s.uv3[idx];
    vec.w = Mathf.Lerp(vec.w, (float)v, r);
    s.uv3[idx] = vec;
 }
 static void UV3_AsColor(PaintJob j, int idx, ref object v, float r)
 {
    var s = j.stream;
    Color c = (Color)v;
    Vector4 asVector = new Vector4(c.r, c.g, c.b, c.a);
    s.uv3[idx] = Vector4.Lerp(s.uv3[idx], asVector, r);
 }