269 lines
9.5 KiB
C#
269 lines
9.5 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using System;
|
|
using System.IO;
|
|
|
|
namespace Obi
|
|
{
|
|
[CustomEditor(typeof(ObiMeshBasedActorBlueprint), true)]
|
|
public abstract class ObiMeshBasedActorBlueprintEditor : ObiActorBlueprintEditor
|
|
{
|
|
|
|
[Flags]
|
|
public enum ParticleCulling
|
|
{
|
|
Off = 0,
|
|
Back = 1 << 0,
|
|
Front = 1 << 1
|
|
}
|
|
|
|
protected Mesh visualizationMesh;
|
|
public ParticleCulling particleCulling = ParticleCulling.Back;
|
|
|
|
protected Material gradientMaterial;
|
|
protected Material textureExportMaterial;
|
|
protected Material paddingMaterial;
|
|
|
|
public override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
gradientMaterial = Resources.Load<Material>("PropertyGradientMaterial");
|
|
textureExportMaterial = Resources.Load<Material>("UVSpaceColorMaterial");
|
|
paddingMaterial = Resources.Load<Material>("PaddingMaterial");
|
|
}
|
|
|
|
public abstract Mesh sourceMesh
|
|
{
|
|
get;
|
|
}
|
|
|
|
protected void NonReadableMeshWarning(Mesh mesh)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
Texture2D icon = EditorGUIUtility.Load("icons/console.erroricon.png") as Texture2D;
|
|
EditorGUILayout.LabelField(new GUIContent("The input mesh is not readable. Read/Write must be enabled in the mesh import settings.", icon), EditorStyles.wordWrappedMiniLabel);
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("Fix now", GUILayout.MaxWidth(100), GUILayout.MinHeight(32)))
|
|
{
|
|
string assetPath = AssetDatabase.GetAssetPath(mesh);
|
|
ModelImporter modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
|
|
if (modelImporter != null)
|
|
{
|
|
modelImporter.isReadable = true;
|
|
}
|
|
modelImporter.SaveAndReimport();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
protected override bool ValidateBlueprint()
|
|
{
|
|
if (sourceMesh != null)
|
|
{
|
|
if (!sourceMesh.isReadable)
|
|
{
|
|
NonReadableMeshWarning(sourceMesh);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public abstract int VertexToParticle(int vertexIndex);
|
|
|
|
public override void UpdateParticleVisibility()
|
|
{
|
|
if (sourceMesh != null && Camera.current != null)
|
|
{
|
|
Vector3[] meshNormals = sourceMesh.normals;
|
|
for (int i = 0; i < sourceMesh.vertexCount; i++)
|
|
{
|
|
int particle = VertexToParticle(i);
|
|
|
|
if (particle >= 0 && particle < blueprint.positions.Length)
|
|
{
|
|
Vector3 camToParticle = Camera.current.transform.position - blueprint.positions[particle];
|
|
|
|
sqrDistanceToCamera[particle] = camToParticle.sqrMagnitude;
|
|
|
|
switch (particleCulling)
|
|
{
|
|
case ParticleCulling.Off:
|
|
visible[particle] = true;
|
|
break;
|
|
case ParticleCulling.Back:
|
|
visible[particle] = Vector3.Dot(meshNormals[i], camToParticle) > 0;
|
|
break;
|
|
case ParticleCulling.Front:
|
|
visible[particle] = Vector3.Dot(meshNormals[i], camToParticle) <= 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ((renderModeFlags & 1) != 0)
|
|
Refresh();
|
|
}
|
|
|
|
}
|
|
|
|
public void DrawGradientMesh(float[] vertexWeights = null, float[] wireframeWeights = null)
|
|
{
|
|
visualizationMesh = GameObject.Instantiate(sourceMesh);
|
|
|
|
if (gradientMaterial.SetPass(0))
|
|
{
|
|
var matrix = Matrix4x4.TRS(Vector3.zero, (blueprint as ObiMeshBasedActorBlueprint).rotation, (blueprint as ObiMeshBasedActorBlueprint).scale);
|
|
|
|
Color[] colors = new Color[visualizationMesh.vertexCount];
|
|
for (int i = 0; i < colors.Length; i++)
|
|
{
|
|
int particle = VertexToParticle(i);
|
|
if (particle >= 0 && particle < blueprint.particleCount)
|
|
{
|
|
float weight = 1;
|
|
if (vertexWeights != null)
|
|
weight = vertexWeights[particle];
|
|
|
|
colors[i] = weight * currentProperty.ToColor(particle);
|
|
}
|
|
else
|
|
colors[i] = Color.gray;
|
|
}
|
|
|
|
visualizationMesh.colors = colors;
|
|
Graphics.DrawMeshNow(visualizationMesh, matrix);
|
|
|
|
Color wireColor = ObiEditorSettings.GetOrCreateSettings().brushWireframeColor;
|
|
|
|
if (gradientMaterial.SetPass(1))
|
|
{
|
|
for (int i = 0; i < colors.Length; i++)
|
|
{
|
|
int particle = VertexToParticle(i);
|
|
if (particle >= 0 && particle < blueprint.particleCount)
|
|
{
|
|
if (wireframeWeights != null)
|
|
colors[i] = wireColor * wireframeWeights[particle];
|
|
else
|
|
colors[i] = wireColor;
|
|
}
|
|
else
|
|
colors[i] = Color.gray;
|
|
}
|
|
|
|
visualizationMesh.colors = colors;
|
|
GL.wireframe = true;
|
|
Graphics.DrawMeshNow(visualizationMesh, matrix);
|
|
GL.wireframe = false;
|
|
}
|
|
|
|
}
|
|
|
|
GameObject.DestroyImmediate(visualizationMesh);
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads particle data from a 2D texture. Can be used to adjust per particle mass, skin radius, etc. using
|
|
* a texture instead of painting it in the editor.
|
|
*
|
|
* Will call onReadProperty once for each particle, passing the particle index and the bilinearly interpolated
|
|
* color of the texture at its coordinate.
|
|
*
|
|
* Be aware that, if a particle corresponds to more than
|
|
* one physical vertex and has multiple uv coordinates,
|
|
* onReadProperty will be called multiple times for that particle.
|
|
*/
|
|
public bool ReadParticlePropertyFromTexture(Texture2D source, Action<int, Color> onReadProperty)
|
|
{
|
|
|
|
if (source == null || onReadProperty == null)
|
|
return false;
|
|
|
|
Vector2[] uvs = sourceMesh.uv;
|
|
|
|
// Iterate over all vertices in the mesh reading back colors from the texture:
|
|
for (int i = 0; i < sourceMesh.vertexCount; ++i)
|
|
{
|
|
try
|
|
{
|
|
onReadProperty(VertexToParticle(i), source.GetPixelBilinear(uvs[i].x, uvs[i].y));
|
|
}
|
|
catch (UnityException e)
|
|
{
|
|
Debug.LogException(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool WriteParticlePropertyToTexture(string path, int width, int height, int padding)
|
|
{
|
|
|
|
if (path == null || textureExportMaterial == null || !textureExportMaterial.SetPass(0))
|
|
return false;
|
|
|
|
if (visualizationMesh == null)
|
|
{
|
|
visualizationMesh = GameObject.Instantiate(sourceMesh);
|
|
}
|
|
|
|
RenderTexture tempRT = RenderTexture.GetTemporary(width, height, 0);
|
|
RenderTexture paddingRT = RenderTexture.GetTemporary(width, height, 0);
|
|
|
|
RenderTexture old = RenderTexture.active;
|
|
RenderTexture.active = tempRT;
|
|
|
|
GL.PushMatrix();
|
|
GL.LoadProjectionMatrix(Matrix4x4.Ortho(0, 1, 0, 1, -1, 1));
|
|
|
|
Color[] colors = new Color[sourceMesh.vertexCount];
|
|
for (int i = 0; i < colors.Length; i++)
|
|
colors[i] = currentProperty.ToColor(VertexToParticle(i));
|
|
|
|
visualizationMesh.colors = colors;
|
|
Graphics.DrawMeshNow(visualizationMesh, Matrix4x4.identity);
|
|
|
|
GL.PopMatrix();
|
|
|
|
// Perform padding/edge dilation
|
|
paddingMaterial.SetFloat("_Padding", padding);
|
|
Graphics.Blit(tempRT, paddingRT, paddingMaterial);
|
|
|
|
// Read result into our Texture2D.
|
|
RenderTexture.active = paddingRT;
|
|
Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
|
|
texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
|
|
|
|
RenderTexture.active = old;
|
|
RenderTexture.ReleaseTemporary(paddingRT);
|
|
RenderTexture.ReleaseTemporary(tempRT);
|
|
|
|
byte[] png = texture.EncodeToPNG();
|
|
GameObject.DestroyImmediate(texture);
|
|
|
|
try
|
|
{
|
|
File.WriteAllBytes(path, png);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
return false;
|
|
}
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|