diff --git a/Editor/VertexPainterWindow_GUI.cs b/Editor/VertexPainterWindow_GUI.cs index 8ae5bdb..dfaea23 100644 --- a/Editor/VertexPainterWindow_GUI.cs +++ b/Editor/VertexPainterWindow_GUI.cs @@ -153,13 +153,33 @@ void DrawChannelGUI() if (enabled != oldEnabled) { InitMeshes(); + UpdateDisplayMode(); } var oldShow = showVertexShader; + EditorGUILayout.BeginHorizontal(); showVertexShader = GUILayout.Toggle(showVertexShader, "Show Vertex Data (ctrl-V)"); if (oldShow != showVertexShader) { UpdateDisplayMode(); } + bool emptyStreams = false; + for (int i = 0; i < jobs.Length; ++i) + { + if (!jobs[i].HasStream()) + emptyStreams = true; + } + if (emptyStreams) + { + if (GUILayout.Button("Add Streams")) + { + for (int i = 0; i < jobs.Length; ++i) + { + jobs[i].EnforceStream(); + } + UpdateDisplayMode(); + } + } + EditorGUILayout.EndHorizontal(); brushVisualization = (BrushVisualization)EditorGUILayout.EnumPopup("Brush Visualization", brushVisualization); @@ -184,14 +204,14 @@ void DrawChannelGUI() bool hasUV3 = false; bool hasPositions = false; bool hasNormals = false; - + bool hasStream = false; for (int i = 0; i < jobs.Length; ++i) { var stream = jobs[i]._stream; if (stream != null) { int vertexCount = jobs[i].verts.Length; - + hasStream = true; hasColors = (stream.colors != null && stream.colors.Length == vertexCount); hasUV0 = (stream.uv0 != null && stream.uv0.Count == vertexCount); hasUV1 = (stream.uv1 != null && stream.uv1.Count == vertexCount); @@ -201,90 +221,109 @@ void DrawChannelGUI() hasNormals = (stream.normals != null && stream.normals.Length == vertexCount); } } - - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel("Clear Channel:"); - if (hasColors && DrawClearButton("Colors")) + + if (hasStream && (hasColors || hasUV0 || hasUV1 || hasUV2 || hasUV3 || hasPositions || hasNormals)) { - for (int i = 0; i < jobs.Length; ++i) + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel("Clear Channel:"); + if (hasColors && DrawClearButton("Colors")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - var stream = jobs[i].stream; - stream.colors = null; - stream.Apply(); + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + var stream = jobs[i].stream; + stream.colors = null; + stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); - } - if (hasUV0 && DrawClearButton("UV0")) - { - for (int i = 0; i < jobs.Length; ++i) + if (hasUV0 && DrawClearButton("UV0")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - var stream = jobs[i].stream; - stream.uv0 = null; - stream.Apply(); + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + var stream = jobs[i].stream; + stream.uv0 = null; + stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); - } - if (hasUV1 && DrawClearButton("UV1")) - { - for (int i = 0; i < jobs.Length; ++i) + if (hasUV1 && DrawClearButton("UV1")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - var stream = jobs[i].stream; - stream.uv1 = null; - stream.Apply(); + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + var stream = jobs[i].stream; + stream.uv1 = null; + stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); - } - if (hasUV2 && DrawClearButton("UV2")) - { - for (int i = 0; i < jobs.Length; ++i) + if (hasUV2 && DrawClearButton("UV2")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - var stream = jobs[i].stream; - stream.uv2 = null; - stream.Apply(); + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + var stream = jobs[i].stream; + stream.uv2 = null; + stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); - } - if (hasUV3 && DrawClearButton("UV3")) - { - for (int i = 0; i < jobs.Length; ++i) + if (hasUV3 && DrawClearButton("UV3")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - var stream = jobs[i].stream; - stream.uv3 = null; - stream.Apply(); + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + var stream = jobs[i].stream; + stream.uv3 = null; + stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); - } - if (hasPositions && DrawClearButton("Pos")) - { - for (int i = 0; i < jobs.Length; ++i) + if (hasPositions && DrawClearButton("Pos")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - jobs[i].stream.positions = null; - Mesh m = jobs[i].stream.GetModifierMesh(); - if (m != null) - m.vertices = jobs[i].meshFilter.sharedMesh.vertices; - jobs[i].stream.Apply(); + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + jobs[i].stream.positions = null; + Mesh m = jobs[i].stream.GetModifierMesh(); + if (m != null) + m.vertices = jobs[i].meshFilter.sharedMesh.vertices; + jobs[i].stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + if (hasNormals && DrawClearButton("Norm")) + { + for (int i = 0; i < jobs.Length; ++i) + { + Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); + jobs[i].stream.normals = null; + jobs[i].stream.tangents = null; + jobs[i].stream.Apply(); + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + EditorGUILayout.EndHorizontal(); } - if (hasNormals && DrawClearButton("Norm")) + else if (hasStream) { - for (int i = 0; i < jobs.Length; ++i) + if (GUILayout.Button("Remove Unused Stream Components")) { - Undo.RecordObject(jobs[i].stream, "Vertex Painter Clear"); - jobs[i].stream.normals = null; - jobs[i].stream.tangents = null; - jobs[i].stream.Apply(); + RevertMat(); + for (int i = 0; i < jobs.Length; ++i) + { + if (jobs[i].HasStream()) + { + DestroyImmediate(jobs[i].stream); + } + } + UpdateDisplayMode(); } - Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); } - - EditorGUILayout.EndHorizontal(); + EditorGUILayout.Separator(); GUILayout.Box("", new GUILayoutOption[]{GUILayout.ExpandWidth(true), GUILayout.Height(1)}); diff --git a/Editor/VertexPainterWindow_Painting.cs b/Editor/VertexPainterWindow_Painting.cs index f71fd5c..5e60d5b 100644 --- a/Editor/VertexPainterWindow_Painting.cs +++ b/Editor/VertexPainterWindow_Painting.cs @@ -1208,6 +1208,31 @@ public Vector4 GetTangent(int i) } public bool HasStream() { return _stream != null; } + public bool HasData() + { + if (_stream == null) + return false; + + int vertexCount = verts.Length; + bool hasColors = (stream.colors != null && stream.colors.Length == vertexCount); + bool hasUV0 = (stream.uv0 != null && stream.uv0.Count == vertexCount); + bool hasUV1 = (stream.uv1 != null && stream.uv1.Count == vertexCount); + bool hasUV2 = (stream.uv2 != null && stream.uv2.Count == vertexCount); + bool hasUV3 = (stream.uv3 != null && stream.uv3.Count == vertexCount); + bool hasPositions = (stream.positions != null && stream.positions.Length == vertexCount); + bool hasNormals = (stream.normals != null && stream.normals.Length == vertexCount); + + return (hasColors || hasUV0 || hasUV1 || hasUV2 || hasUV3 || hasPositions || hasNormals); + } + + public void EnforceStream() + { + if (_stream == null && renderer != null && meshFilter != null) + { + _stream = meshFilter.gameObject.AddComponent(); + } + } + public VertexInstanceStream stream { get @@ -1288,6 +1313,21 @@ public PaintJob(MeshFilter mf, Renderer r) { meshFilter = mf; renderer = r; + _stream = r.gameObject.GetComponent(); + verts = mf.sharedMesh.vertices; + normals = mf.sharedMesh.normals; + tangents = mf.sharedMesh.tangents; + // optionally defer this unless the brush is set to position.. + InitMeshConnections(); + } + + public void CaptureMat() + { + var r = renderer; + if (r == null || stream == null) + { + return; + } if (r.sharedMaterials != null && r.sharedMaterials.Length > 1) { stream.originalMaterial = new Material[r.sharedMaterials.Length]; @@ -1301,20 +1341,17 @@ public PaintJob(MeshFilter mf, Renderer r) stream.originalMaterial = new Material[1]; stream.originalMaterial[0] = r.sharedMaterial; } - verts = mf.sharedMesh.vertices; - normals = mf.sharedMesh.normals; - tangents = mf.sharedMesh.tangents; - // optionally defer this unless the brush is set to position.. - InitMeshConnections(); } } + + public void RevertMat() { // revert old materials for (int i = 0; i < jobs.Length; ++i) { - if (jobs[i].renderer != null) + if (jobs[i].renderer != null && jobs[i].HasStream() && jobs[i].stream.originalMaterial != null && jobs[i].stream.originalMaterial.Length > 0) { var j = jobs[i]; if (j.renderer.sharedMaterials != null && j.stream.originalMaterial != null && @@ -1362,9 +1399,9 @@ void InitMeshes() UpdateDisplayMode(); } - void UpdateDisplayMode() + void UpdateDisplayMode(bool endPainting = true) { - if (painting) + if (painting && endPainting) { EndStroke(); } @@ -1378,9 +1415,9 @@ void UpdateDisplayMode() var job = jobs[i]; EditorUtility.SetSelectedWireframeHidden(job.renderer, hideMeshWireframe); - if (!showVertexShader) + if (!showVertexShader || !enabled) { - if (job.renderer) + if (job.renderer != null && job.HasStream() && job.stream.originalMaterial != null && job.stream.originalMaterial.Length > 0) { if (job.renderer.sharedMaterials != null && job.renderer.sharedMaterials.Length > 1 && job.renderer.sharedMaterials.Length == job.stream.originalMaterial.Length) @@ -1401,27 +1438,33 @@ void UpdateDisplayMode() } else { - if (job.renderer != null) + if (job.renderer != null && job.HasStream()) { - if (job.renderer.sharedMaterials != null && job.renderer.sharedMaterials.Length > 1) + if (job.renderer.sharedMaterial != VertexInstanceStream.vertexShaderMat) { - Material[] mats = new Material[job.renderer.sharedMaterials.Length]; - for (int x = 0; x < job.renderer.sharedMaterials.Length; ++x) - { - mats[x] = VertexInstanceStream.vertexShaderMat; - } - job.renderer.sharedMaterials = mats; + job.CaptureMat(); } - else + if (job.stream.originalMaterial != null && job.stream.originalMaterial.Length > 0) { - job.renderer.sharedMaterial = VertexInstanceStream.vertexShaderMat; + if (job.renderer.sharedMaterials != null && job.renderer.sharedMaterials.Length > 1) + { + Material[] mats = new Material[job.renderer.sharedMaterials.Length]; + for (int x = 0; x < job.renderer.sharedMaterials.Length; ++x) + { + mats[x] = VertexInstanceStream.vertexShaderMat; + } + job.renderer.sharedMaterials = mats; + } + else + { + job.renderer.sharedMaterial = VertexInstanceStream.vertexShaderMat; + } + VertexInstanceStream.vertexShaderMat.SetInt("_flowVisualization", (int)flowVisualization); + VertexInstanceStream.vertexShaderMat.SetInt("_tab", (int)tab); + VertexInstanceStream.vertexShaderMat.SetInt("_flowTarget", (int)flowTarget); + VertexInstanceStream.vertexShaderMat.SetInt("_channel", (int)brushMode); + VertexInstanceStream.vertexShaderMat.SetVector("_uvRange", uvVisualizationRange); } - VertexInstanceStream.vertexShaderMat.SetInt("_flowVisualization", (int)flowVisualization); - VertexInstanceStream.vertexShaderMat.SetInt("_tab", (int)tab); - VertexInstanceStream.vertexShaderMat.SetInt("_flowTarget", (int)flowTarget); - VertexInstanceStream.vertexShaderMat.SetInt("_channel", (int)brushMode); - VertexInstanceStream.vertexShaderMat.SetVector("_uvRange", uvVisualizationRange); - } } } @@ -1641,6 +1684,7 @@ void PrepBrushMode(PaintJob j) { InitPositions(j); InitNormalTangent(j); + UpdateDisplayMode(false); return; } if (tab == Tab.Flow) @@ -1675,6 +1719,7 @@ void PrepBrushMode(PaintJob j) break; } } + UpdateDisplayMode(false); return; } @@ -1760,14 +1805,21 @@ void PrepBrushMode(PaintJob j) } } - + UpdateDisplayMode(false); } void DrawVertexPoints(PaintJob j, Vector3 point) { Profiler.BeginSample("Draw Vertex Points"); - PrepBrushMode(j); + if (j.HasStream() && j.HasData()) + { + PrepBrushMode(j); + } + if (j.renderer == null) + { + return; + } // 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 @@ -1947,8 +1999,11 @@ void EndStroke() for (int i = 0; i < jobs.Length; ++i) { PaintJob j = jobs[i]; - EditorUtility.SetDirty(j.stream); - EditorUtility.SetDirty(j.stream.gameObject); + if (j.HasStream()) + { + EditorUtility.SetDirty(j.stream); + EditorUtility.SetDirty(j.stream.gameObject); + } } } @@ -2172,8 +2227,24 @@ void OnSceneGUI(SceneView sceneView) RaycastHit hit; float distance = float.MaxValue; Vector3 mousePosition = Event.current.mousePosition; - mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y; + float mult = 1; + /* + // WTF, for some reason, in Unity 5.4.1p3 I need to multiply the mouse coordinated by 2 + // for them to line up. + // + // And then, this stopped being true after a while.. WTF? + // + float mult = 2; + if (Application.unityVersion.StartsWith("5.3")) + { + mult = 1; + } + */ + + + mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y * mult; + mousePosition.x *= mult; Vector3 fakeMP = mousePosition; fakeMP.z = 20; Vector3 point = sceneView.camera.ScreenToWorldPoint(fakeMP); @@ -2216,10 +2287,10 @@ void OnSceneGUI(SceneView sceneView) if (jobs[i].HasStream()) { msh = jobs[i].stream.GetModifierMesh(); - if (msh == null) - { - msh = jobs[i].meshFilter.sharedMesh; - } + } + if (msh == null) + { + msh = jobs[i].meshFilter.sharedMesh; } if (RXLookingGlass.IntersectRayMesh(ray, msh, mtx, out hit)) @@ -2362,6 +2433,7 @@ void OnSceneGUI(SceneView sceneView) jobEdits[i] = true; Undo.RegisterCompleteObjectUndo(jobs[i].stream, "Vertex Painter Stroke"); } + PaintMesh(jobs[i], point, lerper, value); Undo.RecordObject(jobs[i].stream, "Vertex Painter Stroke"); diff --git a/LICENSE.txt b/LICENSE.txt index 0b2997e..c48813f 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -12,7 +12,7 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. No portion of the software may be sold or included in packages in the Unity -Asset Store. +Asset Store without express permission from the author. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/VertexInstanceStream.cs b/VertexInstanceStream.cs index c05556a..322d6e0 100644 --- a/VertexInstanceStream.cs +++ b/VertexInstanceStream.cs @@ -73,15 +73,14 @@ public Color[] colors public Material[] originalMaterial; public static Material vertexShaderMat; - #endif - void Start() + + void Awake() { // restore original material if we got saved with the preview material. // I tried to do this in a number of ways; using the pre/post serialization callbacks seemed // like the best, but is actually not possible because they don't always both get called. In editor, // sometimes only the pre-serialization callback gets called. WTF.. - #if UNITY_EDITOR MeshRenderer mr = GetComponent(); if (mr != null) { @@ -95,12 +94,17 @@ void Start() } mr.sharedMaterials = mats; } - else if (originalMaterial.Length > 0) + else if (originalMaterial != null && originalMaterial.Length > 0) { mr.sharedMaterial = originalMaterial[0]; } } - #endif + } + #endif + + void Start() + { + Apply(true); }