// 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(VertexPainterWindow.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; }
public void OnGUI(PaintJob[] jobs) { var window = VertexPainterWindow.GetWindow <VertexPainterWindow>(); window.brushMode = (VertexPainterWindow.BrushTarget)EditorGUILayout.EnumPopup("Target Channel", window.brushMode); aoOneMinusColor = EditorGUILayout.Toggle("Color Inverse (1-v)", aoOneMinusColor); 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); //aoColliderList = EditorGUILayout.ObjectField(aoColliderList); EditorGUILayout.LabelField("Colliders: " + aoColliderList.Count.ToString()); foreach (var go in aoColliderList) { EditorGUILayout.ObjectField("", go, typeof(GameObject), false); } EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Add Selected Object")) { aoColliderList.AddRange(Selection.gameObjects); } if (GUILayout.Button("Clear")) { aoColliderList.Clear(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); if (GUILayout.Button("Bake")) { DoBakeAO(jobs, window); } }
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); } }
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 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(); }