// #define POLYBRUSH_DEBUG
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Polybrush;
namespace UnityEditor.Polybrush
{
///
/// Vertex texture painter brush mode.
/// Similar to BrushModePaint, except it packs blend information into both the color32 and UV3/4 channels.
///
internal class BrushModeTexture : BrushModeMesh
{
static class Styles
{
internal static readonly GUIContent[] k_ModeIcons = new GUIContent[]
{
new GUIContent("Brush", "Brush" ),
new GUIContent("Fill", "Fill" ),
new GUIContent("Flood", "Flood" )
};
internal static readonly string[] k_MeshChannel =
{
"Color",
"Tangent",
"UV1",
"UV2",
"UV3",
"UV4"
};
internal static MeshChannel ToMeshChannel(int value)
{
switch (value)
{
case 0:
return MeshChannel.Color;
case 1:
return MeshChannel.Tangent;
case 2:
return MeshChannel.UV0;
case 3:
return MeshChannel.UV2;
case 4:
return MeshChannel.UV3;
case 5:
return MeshChannel.UV4;
}
return MeshChannel.Null;
}
internal static int ToInt(MeshChannel channel)
{
switch (channel)
{
case MeshChannel.Color:
return 0;
case MeshChannel.Tangent:
return 1;
case MeshChannel.UV0:
return 2;
case MeshChannel.UV2:
return 3;
case MeshChannel.UV3:
return 4;
case MeshChannel.UV4:
return 5;
}
return 0;
}
}
enum PanelView
{
Paint,
Configuration,
}
class EditableObjectData
{
public int VertexCount;
public EditableObject CacheTarget;
public List CacheMaterials;
public SplatSet SplatCache,
SplatTarget,
SplatErase,
SplatCurrent;
public Dictionary> TriangleLookup;
}
internal SplatWeight brushColor = null;
SplatWeight m_MinWeights = null;
[SerializeField]
int m_SelectedAttributeIndex = 0;
int selectedAttributeIndex
{
get { return m_SelectedAttributeIndex; }
set
{
m_SelectedAttributeIndex = Mathf.Clamp(value, 0, meshAttributes.Length-1);
}
}
bool m_LikelySupportsTextureBlending = true;
AttributeLayoutContainer m_MeshAttributesContainer = null;
List m_MeshAttributesContainers = new List();
string[] m_AvailableMaterialsAsString = { };
EditableObject m_MainCacheTarget = null;
List m_MainCacheMaterials = new List();
Dictionary m_EditableObjectsData = new Dictionary();
// temp vars
PolyEdge[] m_FillModeEdges = new PolyEdge[3];
List m_FillModeAdjacentTriangles = null;
internal int m_CurrentMeshACIndex = 0;
internal PaintMode m_PaintMode = PaintMode.Brush;
PanelView m_CurrentPanelView = PanelView.Paint;
internal AttributeLayout[] meshAttributes
{
get
{
return m_MeshAttributesContainer != null ? m_MeshAttributesContainer.attributes : null;
}
}
// The message that will accompany Undo commands for this brush. Undo/Redo is handled by PolyEditor.
internal override string UndoMessage { get { return "Paint Brush"; } }
protected override string ModeSettingsHeader { get { return "Texture Paint Settings"; } }
protected override string DocsLink { get { return PrefUtility.documentationTextureBrushLink; } }
internal override void OnEnable()
{
base.OnEnable();
m_CurrentPanelView = PanelView.Paint;
m_LikelySupportsTextureBlending = false;
m_MeshAttributesContainer = null;
brushColor = null;
RebuildMaterialCaches();
if (meshAttributes != null)
OnMaterialSelected();
foreach(GameObject go in Selection.gameObjects)
m_LikelySupportsTextureBlending = CheckForTextureBlendSupport(go);
}
internal override bool SetDefaultSettings()
{
m_PaintMode = PaintMode.Brush;
return true;
}
// Inspector GUI shown in the Editor window. Base class shows BrushSettings by default
internal override void DrawGUI(BrushSettings brushSettings)
{
base.DrawGUI(brushSettings);
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
m_PaintMode = (PaintMode)GUILayout.Toolbar((int)m_PaintMode, Styles.k_ModeIcons, GUILayout.Width(150));
GUILayout.FlexibleSpace();
}
GUILayout.Space(4);
// Selection dropdown for material (for submeshes)
if (m_AvailableMaterialsAsString.Count() > 0)
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
EditorGUILayout.LabelField("Material", GUILayout.Width(60));
if (m_CurrentPanelView == PanelView.Configuration)
GUI.enabled = false;
m_CurrentMeshACIndex = EditorGUILayout.Popup(m_CurrentMeshACIndex, m_AvailableMaterialsAsString, "Popup");
if (m_CurrentPanelView == PanelView.Configuration)
GUI.enabled = true;
// Buttons to switch between Paint and Configuration views
if (m_CurrentPanelView == PanelView.Paint && GUILayout.Button("Configure", GUILayout.Width(70)))
OpenConfiguration();
else if (m_CurrentPanelView == PanelView.Configuration)
{
if (GUILayout.Button("Revert", GUILayout.Width(70)))
CloseConfiguration(false);
if (GUILayout.Button("Save", GUILayout.Width(70)))
CloseConfiguration(true);
}
if (EditorGUI.EndChangeCheck())
{
m_MeshAttributesContainer = m_MeshAttributesContainers[m_CurrentMeshACIndex];
OnMaterialSelected();
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space();
if (m_CurrentPanelView == PanelView.Paint)
DrawGUIPaintView();
else if (m_CurrentPanelView == PanelView.Configuration)
{
Material selectedMat = m_MainCacheMaterials[m_CurrentMeshACIndex];
string[] names = selectedMat.GetTexturePropertyNames();
using (new GUILayout.VerticalScope())
{
for (int i = 0; i < names.Length; ++i)
{
string n = names[i];
if (selectedMat.HasProperty(n))
DrawConfigurationPanel(GetPropertyInfo(n), n, selectedMat);
}
}
}
}
struct MaterialPropertyInfo
{
public string PropertyName;
public bool LinkedToAttributesLayout;
public bool IsVisible;
}
List materialPropertiesCache = new List();
AttributeLayoutContainer m_LoadedAttributes = null;
private MaterialPropertyInfo GetPropertyInfo(string name)
{
MaterialPropertyInfo res = default(MaterialPropertyInfo);
foreach (MaterialPropertyInfo p in materialPropertiesCache)
{
if (p.PropertyName == name)
{
res = p;
break;
}
}
return res;
}
private void UpdatePropertyInfo(MaterialPropertyInfo pUpdate)
{
int index = materialPropertiesCache.FindIndex(0, p => p.PropertyName == pUpdate.PropertyName);
if (index >= 0)
materialPropertiesCache[index] = pUpdate;
}
private void OnMaterialSelected()
{
Material selectedMat = m_MainCacheMaterials[m_CurrentMeshACIndex];
string[] names = selectedMat.GetTexturePropertyNames();
materialPropertiesCache.Clear();
foreach (string n in names)
{
if (selectedMat.HasProperty(n))
{
materialPropertiesCache.Add(new MaterialPropertyInfo()
{
PropertyName = n,
LinkedToAttributesLayout = false,
IsVisible = true
});
}
}
}
private void OpenConfiguration()
{
m_CurrentPanelView = PanelView.Configuration;
if (m_MainCacheMaterials == null || m_CurrentMeshACIndex < 0 || m_CurrentMeshACIndex >= m_MainCacheMaterials.Count)
return;
Material mat = m_MainCacheMaterials[m_CurrentMeshACIndex];
Shader shader = mat.shader;
if (ShaderMetaDataUtility.IsValidShader(shader))
{
#pragma warning disable 0618
// Data conversion between Polybrush Beta and Polybrush 1.0.
string path = ShaderMetaDataUtility.FindPolybrushMetaDataForShader(shader);
if (!string.IsNullOrEmpty(path))
ShaderMetaDataUtility.ConvertMetaDataToNewFormat(shader);
#pragma warning restore 0618
m_LoadedAttributes = ShaderMetaDataUtility.LoadShaderMetaData(shader);
}
}
private void CloseConfiguration(bool saveOnDisk)
{
if (saveOnDisk)
{
Material mat = m_MainCacheMaterials[m_CurrentMeshACIndex];
Shader shader = mat.shader;
ShaderMetaDataUtility.SaveShaderMetaData(shader, m_LoadedAttributes);
foreach(GameObject go in Selection.gameObjects)
m_LikelySupportsTextureBlending = CheckForTextureBlendSupport(go);
}
m_LoadedAttributes = null;
m_CurrentPanelView = PanelView.Paint;
}
private void DrawGUIPaintView()
{
if (meshAttributes != null)
{
RefreshPreviewTextureCache();
int prevSelectedAttributeIndex = selectedAttributeIndex;
selectedAttributeIndex =
SplatWeightEditor.OnInspectorGUI(selectedAttributeIndex, ref brushColor, meshAttributes);
if (prevSelectedAttributeIndex != selectedAttributeIndex)
SetBrushColorWithAttributeIndex(selectedAttributeIndex);
#if POLYBRUSH_DEBUG
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if(GUILayout.Button("MetaData", EditorStyles.miniButton))
{
Debug.Log(meshAttributes.ToString("\n"));
string str = EditorUtility.FindPolybrushMetaDataForShader(meshAttributesContainer.shader);
if(!string.IsNullOrEmpty(str))
{
TextAsset asset = AssetDatabase.LoadAssetAtPath(str);
if(asset != null)
EditorGUIUtility.PingObject(asset);
else
Debug.LogWarning("No MetaData found for Shader \"" + meshAttributesContainer.shader.name + "\"");
}
else
{
Debug.LogWarning("No MetaData found for Shader \"" + meshAttributesContainer.shader.name + "\"");
}
}
GUILayout.EndHorizontal();
GUILayout.Space(4);
if(GUILayout.Button("rebuild targets"))
RebuildColorTargets(brushColor, brushSettings.strength);
GUILayout.Label(brushColor != null ? brushColor.ToString() : "brush color: null\n");
#endif
}
else
{
if (!m_LikelySupportsTextureBlending)
{
EditorGUILayout.HelpBox("It doesn't look like any of the materials on this object support texture blending!\n\nSee the readme for information on creating custom texture blend shaders.", MessageType.Warning);
}
}
}
private void DrawConfigurationPanel(MaterialPropertyInfo guiInfo, string propertyName, Material mat)
{
using (new GUILayout.HorizontalScope("box"))
{
using (new GUILayout.VerticalScope())
{
EditorGUI.BeginChangeCheck();
using (new GUILayout.HorizontalScope())
{
if (!m_LoadedAttributes.HasAttributes(propertyName))
{
GUILayout.Label(propertyName, GUILayout.Width(100));
GUILayout.FlexibleSpace();
if (GUILayout.Button("Create attributes",GUILayout.Width(120), GUILayout.Height(16)))
{
AttributeLayout newAttr = new AttributeLayout();
newAttr.propertyTarget = propertyName;
m_LoadedAttributes.AddAttribute(newAttr);
}
}
else
{
guiInfo.IsVisible = EditorGUILayout.Foldout(guiInfo.IsVisible, propertyName, true);
if (GUILayout.Button("Erase attributes", GUILayout.ExpandWidth(true), GUILayout.MinWidth(60), GUILayout.MaxWidth(120), GUILayout.Height(16)))
m_LoadedAttributes.RemoveAttribute(propertyName);
}
}
if (EditorGUI.EndChangeCheck())
{
UpdatePropertyInfo(guiInfo);
}
if (m_LoadedAttributes.HasAttributes(propertyName) && guiInfo.IsVisible)
{
AttributeLayout attr = m_LoadedAttributes.GetAttributes(propertyName);
EditorGUILayout.Space();
using (new GUILayout.HorizontalScope())
{
using (new GUILayout.VerticalScope(GUILayout.Width(70), GUILayout.ExpandWidth(false)))
{
Texture tex = mat.GetTexture(propertyName);
EditorGUI.DrawPreviewTexture(
EditorGUILayout.GetControlRect(GUILayout.Width(64), GUILayout.Height(64)),
(tex != null) ? tex : Texture2D.blackTexture);
}
using (new GUILayout.VerticalScope())
{
GUILayout.Label("Channel");
GUILayout.Label("Index");
GUILayout.Label("Range");
GUILayout.Label("Group");
GUILayout.Label("Is Base Texture");
}
GUILayout.FlexibleSpace();
using (new GUILayout.VerticalScope())
{
// Channel selection
attr.channel =
Styles.ToMeshChannel(EditorGUILayout.Popup(Styles.ToInt(attr.channel), Styles.k_MeshChannel, GUILayout.Width(140)));
// Index selection
attr.index = (ComponentIndex)GUILayout.Toolbar((int)attr.index, ComponentIndexUtility.ComponentIndexPopupDescriptions, GUILayout.Width(140));
// Value range
attr.range = EditorGUILayout.Vector2Field("", attr.range, GUILayout.Width(140));
// Group selection
attr.mask = EditorGUILayout.Popup(attr.mask, AttributeLayout.DefaultMaskDescriptions, GUILayout.Width(140));
attr.isBaseTexture = EditorGUILayout.Toggle(attr.isBaseTexture);
}
}
}
}
}
}
internal override void OnBrushSettingsChanged(BrushTarget target, BrushSettings settings)
{
base.OnBrushSettingsChanged(target, settings);
RebuildColorTargets(target?.editableObject, brushColor, settings.strength);
//RebuildColorTargets(brushColor, settings.strength);
}
///
/// Test a gameObject and it's mesh renderers for compatible shaders, and if one is found
/// load it's attribute data into meshAttributes.
///
/// The GameObject being checked for texture blend support
///
internal bool CheckForTextureBlendSupport(GameObject go)
{
bool supports = false;
var materials = Util.GetMaterials(go);
m_MeshAttributesContainers.Clear();
Material mat;
List indexes = new List();
for(int i = 0; i < materials.Count; i++)
{
mat = materials[i];
if (ShaderMetaDataUtility.IsValidShader(mat.shader))
{
AttributeLayoutContainer detectedMeshAttributes = ShaderMetaDataUtility.LoadShaderMetaData(mat.shader);
{
if (detectedMeshAttributes != null)
{
m_MeshAttributesContainers.Add(detectedMeshAttributes);
indexes.Add(i);
m_MainCacheMaterials.Add(mat);
supports = true;
}
}
}
}
if (supports)
{
m_MeshAttributesContainer = m_MeshAttributesContainers.First();
foreach(int i in indexes)
ArrayUtility.Add(ref m_AvailableMaterialsAsString, materials[i].name);
}
if (meshAttributes == null)
supports = false;
return supports;
}
internal void SetBrushColorWithAttributeIndex(int index)
{
if( brushColor == null ||
meshAttributes == null)
return;
if(meshAttributes[index].mask > -1)
{
for(int i = 0; i < meshAttributes.Length; i++)
{
brushColor.SetAttributeValue(meshAttributes[i], meshAttributes[i].min);
}
}
brushColor.SetAttributeValue(meshAttributes[index], meshAttributes[index].max);
}
// Called when the mouse begins hovering an editable object.
internal override void OnBrushEnter(EditableObject target, BrushSettings settings)
{
base.OnBrushEnter(target, settings);
if(target.editMesh == null)
return;
bool refresh = (m_MainCacheTarget != null && !m_MainCacheTarget.Equals(target)) || m_MainCacheTarget == null;
if (m_MainCacheTarget != null && m_MainCacheTarget.Equals(target))
{
var targetMaterials = target.gameObjectAttached.GetMaterials();
refresh = !targetMaterials.SequenceEqual(m_MainCacheMaterials);
}
if (refresh)
{
SetActiveObject(target);
RebuildMaterialCaches();
PolybrushEditor.instance.Repaint();
}
if (m_LikelySupportsTextureBlending && (brushColor == null || !brushColor.MatchesAttributes(meshAttributes)))
{
brushColor = new SplatWeight(SplatWeight.GetChannelMap(meshAttributes));
SetBrushColorWithAttributeIndex(selectedAttributeIndex);
}
RebuildColorTargets(target, brushColor, settings.strength);
}
void RebuildMaterialCaches()
{
ArrayUtility.Clear(ref m_AvailableMaterialsAsString);
m_CurrentMeshACIndex = 0;
m_MainCacheMaterials.Clear();
if (m_MainCacheTarget == null)
return;
m_MeshAttributesContainer = null;
m_CurrentMeshACIndex = 0;
m_LikelySupportsTextureBlending = CheckForTextureBlendSupport(m_MainCacheTarget.gameObjectAttached);
}
// Called whenever the brush is moved. Note that @target may have a null editableObject.
internal override void OnBrushMove(BrushTarget target, BrushSettings settings)
{
base.OnBrushMove(target, settings);
if(!Util.IsValid(target) || !m_LikelySupportsTextureBlending || meshAttributes.Length == 0)
return;
if(!m_EditableObjectsData.ContainsKey(target.editableObject))
return;
var data = m_EditableObjectsData[target.editableObject];
if(!data.CacheMaterials.Contains(m_MainCacheMaterials[m_CurrentMeshACIndex]))
return;
bool invert = settings.isUserHoldingControl;
float[] weights;
if(m_PaintMode == PaintMode.Brush)
{
weights = target.GetAllWeights();
}
else if(m_PaintMode == PaintMode.Flood)
{
weights = new float[data.VertexCount];
for(int i = 0; i < data.VertexCount; i++)
weights[i] = 1f;
}
else
{
weights = new float[data.VertexCount];
int[] indices = target.editableObject.editMesh.GetTriangles();
int index = 0;
float weightTarget = 1f;
foreach(PolyRaycastHit hit in target.raycastHits)
{
if(hit.triangle > -1)
{
index = hit.triangle * 3;
m_FillModeEdges[0].x = indices[index+0];
m_FillModeEdges[0].y = indices[index+1];
m_FillModeEdges[1].x = indices[index+1];
m_FillModeEdges[1].y = indices[index+2];
m_FillModeEdges[2].x = indices[index+2];
m_FillModeEdges[2].y = indices[index+0];
for(int i = 0; i < 3; i++)
{
if(data.TriangleLookup.TryGetValue(m_FillModeEdges[i], out m_FillModeAdjacentTriangles))
{
for(int n = 0; n < m_FillModeAdjacentTriangles.Count; n++)
{
index = m_FillModeAdjacentTriangles[n] * 3;
weights[indices[index ]] = weightTarget;
weights[indices[index+1]] = weightTarget;
weights[indices[index+2]] = weightTarget;
}
}
}
}
}
}
int mask = meshAttributes[selectedAttributeIndex].mask;
if(data.SplatCurrent == null)
RebuildCaches(data);
data.SplatCurrent.LerpWeights(data.SplatCache, invert ? data.SplatErase : data.SplatTarget, mask, weights);
data.SplatCurrent.Apply(target.editableObject.editMesh);
target.editableObject.ApplyMeshAttributes();
}
// Called when the mouse exits hovering an editable object.
internal override void OnBrushExit(EditableObject target)
{
base.OnBrushExit(target);
if(!m_EditableObjectsData.ContainsKey(target))
return;
var data = m_EditableObjectsData[target];
if(data.SplatCache != null)
{
data.SplatCache.Apply(target.editMesh);
target.ApplyMeshAttributes();
target.graphicsMesh.UploadMeshData(false);
}
if(m_MainCacheTarget != null && data.CacheTarget.Equals(m_MainCacheTarget))
m_MainCacheTarget = null;
m_EditableObjectsData.Remove(target);
}
// Called every time the brush should apply itself to a valid target. Default is on mouse move.
internal override void OnBrushApply(BrushTarget target, BrushSettings settings)
{
if(!m_LikelySupportsTextureBlending)
return;
var data = m_EditableObjectsData[target.editableObject];
data.SplatCurrent.CopyTo(data.SplatCache);
data.SplatCache.Apply(target.editableObject.editMesh);
MeshChannel channelsChanged =
MeshChannel.Color | MeshChannel.Tangent | MeshChannel.UV0 | MeshChannel.UV2 | MeshChannel.UV3 | MeshChannel.UV4;
target.editableObject.modifiedChannels |= channelsChanged;
base.OnBrushApply(target, settings);
}
// set mesh splat_current back to their original state before registering for undo
internal override void RegisterUndo(BrushTarget brushTarget)
{
if(!m_EditableObjectsData.ContainsKey(brushTarget.editableObject))
return;
var data = m_EditableObjectsData[brushTarget.editableObject];
if(data.SplatCache != null)
{
data.SplatCache.Apply(brushTarget.editableObject.editMesh);
brushTarget.editableObject.ApplyMeshAttributes();
}
base.RegisterUndo(brushTarget);
}
internal override void UndoRedoPerformed(List modified)
{
base.UndoRedoPerformed(modified);
foreach(var data in m_EditableObjectsData)
RebuildCaches(data.Value);
}
internal override void DrawGizmos(BrushTarget target, BrushSettings settings)
{
PolyMesh mesh = target.editableObject.editMesh;
if(Util.IsValid(target) && m_PaintMode == PaintMode.Fill)
{
Vector3[] vertices = mesh.vertices;
int[] indices = mesh.GetTriangles();
using(new Handles.DrawingScope(target.transform.localToWorldMatrix))
{
int index = 0;
var data = m_EditableObjectsData[target.editableObject];
foreach(PolyRaycastHit hit in target.raycastHits)
{
if(hit.triangle > -1)
{
Handles.color = Color.green;
index = hit.triangle * 3;
Handles.DrawLine(vertices[indices[index+0]] + hit.normal * .1f, vertices[indices[index+1]] + hit.normal * .1f);
Handles.DrawLine(vertices[indices[index+1]] + hit.normal * .1f, vertices[indices[index+2]] + hit.normal * .1f);
Handles.DrawLine(vertices[indices[index+2]] + hit.normal * .1f, vertices[indices[index+0]] + hit.normal * .1f);
m_FillModeEdges[0].x = indices[index+0];
m_FillModeEdges[0].y = indices[index+1];
m_FillModeEdges[1].x = indices[index+1];
m_FillModeEdges[1].y = indices[index+2];
m_FillModeEdges[2].x = indices[index+2];
m_FillModeEdges[2].y = indices[index+0];
for(int i = 0; i < 3; i++)
{
if(data.TriangleLookup.TryGetValue(m_FillModeEdges[i], out m_FillModeAdjacentTriangles))
{
for(int n = 0; n < m_FillModeAdjacentTriangles.Count; n++)
{
index = m_FillModeAdjacentTriangles[n] * 3;
Handles.DrawLine(vertices[indices[index+0]] + hit.normal * .1f, vertices[indices[index+1]] + hit.normal * .1f);
Handles.DrawLine(vertices[indices[index+1]] + hit.normal * .1f, vertices[indices[index+2]] + hit.normal * .1f);
Handles.DrawLine(vertices[indices[index+2]] + hit.normal * .1f, vertices[indices[index+0]] + hit.normal * .1f);
}
}
}
}
}
}
}
else
{
base.DrawGizmos(target, settings);
}
}
internal void RefreshPreviewTextureCache()
{
if (meshAttributes != null
&& m_MainCacheMaterials != null)
{
for (int i = 0; i < meshAttributes.Length; ++i)
{
AttributeLayout attributes = meshAttributes[i];
attributes.previewTexture = (Texture2D)m_MainCacheMaterials[m_CurrentMeshACIndex].GetTexture(attributes.propertyTarget);
}
}
}
private AttributeLayout GetBaseTexture()
{
AttributeLayout selectedAttribute = meshAttributes[selectedAttributeIndex];
foreach (var attr in meshAttributes)
{
if (attr.mask != selectedAttribute.mask)
{
continue;
}
if (attr.isBaseTexture)
{
return attr;
}
}
return null;
}
void RebuildColorTargets(EditableObject target, SplatWeight blend, float strength)
{
if(target == null)
return;
var data = m_EditableObjectsData[target];
if (blend == null || data.SplatCache == null || data.SplatTarget == null)
return;
m_MinWeights = data.SplatTarget.GetMinWeights();
data.SplatTarget.LerpWeights(data.SplatCache, blend, strength);
// get index of texture that is being painted
var attrib = meshAttributes[selectedAttributeIndex];
var baseTexture = attrib.isBaseTexture? attrib : GetBaseTexture();
var baseTexIndex = baseTexture != null ? (int)baseTexture.index : -1;
data.SplatErase.LerpWeightOnSingleChannel(data.SplatCache, m_MinWeights, strength, attrib.channel, (int)attrib.index, baseTexIndex);
}
void SetupBaseTextures(SplatSet set)
{
// map base texture index to mask index
// map mask index to indices of other textures in mask
Dictionary> channelsToBaseTex = new Dictionary>();
Dictionary baseTexToMask = new Dictionary();
Dictionary> maskToIndices = new Dictionary>();
foreach(var attr in meshAttributes)
{
if (attr.isBaseTexture)
{
if (channelsToBaseTex.TryGetValue(attr.channel, out List baseTexIndices))
{
baseTexIndices.Add((int)attr.index);
channelsToBaseTex[attr.channel] = baseTexIndices;
}
else
channelsToBaseTex.Add(attr.channel, new List() { (int)attr.index });
baseTexToMask.Add((int)attr.index, attr.mask);
}
else
{
if (maskToIndices.TryGetValue(attr.mask, out List indices))
{
indices.Add((int)attr.index);
maskToIndices[attr.mask] = indices;
}
else
maskToIndices.Add(attr.mask, new List() { (int)attr.index });
}
}
if(baseTexToMask.Count > 0)
set.SetChannelBaseTextureWeights(channelsToBaseTex, baseTexToMask, maskToIndices);
}
void SetActiveObject(EditableObject activeObject)
{
m_MainCacheTarget = activeObject;
EditableObjectData data;
if(!m_EditableObjectsData.TryGetValue(activeObject, out data))
{
data = new EditableObjectData();
m_EditableObjectsData.Add(activeObject, data);
}
data.CacheTarget = activeObject;
data.CacheMaterials = activeObject.gameObjectAttached.GetMaterials();
RebuildCaches(data);
}
void RebuildCaches(EditableObjectData data)
{
PolyMesh mesh = data.CacheTarget.editMesh;
data.VertexCount = mesh.vertexCount;
data.TriangleLookup = PolyMeshUtility.GetAdjacentTriangles(mesh);
if (meshAttributes == null)
{
// clear caches
data.SplatCache = null;
data.SplatCurrent = null;
data.SplatTarget = null;
data.SplatErase = null;
return;
}
data.SplatCache = new SplatSet(mesh, meshAttributes);
SetupBaseTextures(data.SplatCache);
data.SplatCurrent = new SplatSet(data.SplatCache);
data.SplatTarget = new SplatSet(data.VertexCount, meshAttributes);
data.SplatErase = new SplatSet(data.VertexCount, meshAttributes);
}
}
}