NLDClient-yudde/ProjectNLD/Assets/Editor/Scene/SceneBrush/SceneBrush.cs

429 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using UnityEngine;
using Sirenix.OdinInspector;
using TGS;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
//笔刷类型
public enum EBrushType
{
None = 0,
Tree,
Building,
Stuff,
}
//物品分布方式
public enum ESceneObjectLayout
{
None = 0,
RegularPolygon = 1,
Random = 2,
}
/// <summary>
/// 笔刷工具每一个笔刷绑定了一个Prefab和一个SceneObjectRenderInfo
/// 每一个笔刷就对应了一个具体外貌的游戏物体(如绿色的松树和白色的松树就需要两个笔刷)
/// 需要继承自该类完成自己类型笔刷的逻辑
/// </summary>
public abstract partial class SceneBrush : ScriptableObject
{
#region Const
protected const string SCENE_BRUSH_GENERATED_PREFAB_PATH = "Assets/Art/Scene/SceneBrush/Brush/Generated/Prefabs";
protected const string SCENE_BRUSH_GENERATED_MATERIAL_PATH =
"Assets/Art/Scene/SceneBrush/Brush/Generated/Materials";
protected const string SCENE_TRANSPARENT_SHADER = "Assets/Code/Shaders/Scene/NLD_Scene_Transparent.shader";
protected const string SCENE_OPAQUE_SHADER = "Assets/Code/Shaders/Scene/NLD_Scene_Opaque.shader";
public const string BRUSH_UNDO_NAME = "Brush Undo";
public const string BRUSH_DRAW_UNDO_NAME = "Brush Draw Undo";
//public const string ART_NAME = "Art";
public static readonly string[] EBrushTypeStrings = new[]
{
"None",
"树",
"建筑物",
"其他物件",
};
// private Transform _art;
//
// public Transform Art
// {
// get
// {
// if (_art == null)
// {
// var scene = _sceneManager.gameObject.scene;
// var roots = scene.GetRootGameObjects();
// var artGo = roots.FirstOrDefault(r => r.name == ART_NAME);
// if (artGo == null)
// {
// artGo = ObjectFactory.CreateGameObject(scene, HideFlags.None, ART_NAME);
// }
//
// _art = artGo.transform;
// }
//
// return _art;
// }
// }
#endregion
#region Fields And Properties
[Sirenix.OdinInspector.ReadOnly]
public GameObject prefab;
[Sirenix.OdinInspector.ReadOnly]
public SceneObjectRenderInfo renderInfo;
[HideInInspector]
public bool useTGS;
[HideInInspector]
public float radius = 1;
[HideInInspector]
public ESceneObjectLayout layout = ESceneObjectLayout.RegularPolygon;
[HideInInspector]
public int objCount = 1;
[HideInInspector]
public float scale = 1;
[HideInInspector]
public float yAngle;
[HideInInspector]
public bool isEraser;
[HideInInspector]
public bool eraseSameType;
public string remarkName;
[FormerlySerializedAs("checkOverlap")]
[HideInInspector]
public bool canOverlap;
private NLDSceneManager _sceneManager;
private TerrainGridSystem _tgs;
private Vector3 _lastDrawPosition;
public virtual bool supportTransparent
{
get
{
//如果gameobject上所有的材质的shader都是SCENE_OPAQUE_SHADER才支持半透
if (prefab == null)
return false;
var msrs = prefab.GetComponentsInChildren<MeshRenderer>();
bool result = true;
foreach (var msr in msrs)
{
foreach (var mat in msr.sharedMaterials)
{
if (mat != null && AssetDatabase.GetAssetPath(mat.shader) == SCENE_OPAQUE_SHADER)
{
continue;
}
result = false;
break;
}
}
return result;
}
}
public abstract string RootName { get; }
public virtual bool SupportSurface => prefab != null && prefab.GetComponentsInChildren<MeshRenderer>().Length == 1;
public abstract EBrushType brushType { get; }
public bool Valid => prefab != null && renderInfo != null;
public ESceneObjectType SceneObjectType => SceneBrushHelper.BrushType2SceneObjectType(brushType);
#endregion
#region MainLife
public void OnSelected(NLDSceneManager sceneManager, TerrainGridSystem tgs)
{
_sceneManager = sceneManager;
_tgs = tgs;
}
/// <summary>
/// 基类的该函数只能对应大部分的场景物件(如树木,石头等),建筑等特殊的需要重写该函数
/// </summary>
/// <param name="newPrefab"></param>
public virtual void OnPrefabChanged(GameObject newPrefab)
{
if (newPrefab == null)
{
prefab = null;
return;
}
//预处理一下资源
if (!PreProcessPrefab(newPrefab))
return;
var savedBrush = SceneBrushHelper.GetSceneBrushByPrefab(newPrefab);
if (savedBrush != null)
{
//如果当前预制体已经与其他笔刷绑定了
if (!OnPrefabRepeat(ref newPrefab, savedBrush))
return;
}
prefab = newPrefab;
//后处理资源
PostProcessPrefab();
}
//预处理资源如树是从TreeCreator制作的但Unity已经不支持了需要我们手动移除无用组件和材质并设置正确值
protected virtual bool PreProcessPrefab(GameObject k_prefab)
{
return true;
}
protected virtual bool OnPrefabRepeat(ref GameObject k_prefab, SceneBrush savedBrush)
{
var clonedPrefab = ClonePrefab(k_prefab);
k_prefab = clonedPrefab;
return true;
}
//后处理资源,包括生成半透明材质,记录材质和网格信息
protected virtual void PostProcessPrefab()
{
//应用当前不透明材质到renderInfo上
ApplyOpaqueMaterial();
//创建透明材质并应用到renderInfo上
if (supportTransparent)
CreateTransparentMaterialAndApply();
//应用网格
ApplyMesh();
}
protected GameObject ClonePrefab(GameObject k_prefab)
{
if (k_prefab == null || !PrefabUtility.IsPartOfPrefabAsset(k_prefab))
{
Debug.LogError("克隆对象失败!");
return null;
}
var instance = PrefabUtility.InstantiatePrefab(k_prefab) as GameObject;
if (instance == null)
return null;
PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
var newPath =
EditorUtil.GetAssetUniquePath($"{SCENE_BRUSH_GENERATED_PREFAB_PATH}/{name}.prefab", out var assetName);
instance.name = assetName;
PrefabUtility.SaveAsPrefabAsset(instance, newPath);
DestroyImmediate(instance);
var newPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(newPath);
//深拷贝材质
var mats = MaterialUtil.ReplaceMaterialOnGameObject(newPrefab);
if (mats == null)
return null;
foreach (var mat in mats)
{
EditorUtil.CreateAnUniqueAsset(mat, $"{SCENE_BRUSH_GENERATED_MATERIAL_PATH}/{name}_opaque.mat");
}
AssetDatabase.Refresh();
return newPrefab;
}
protected void ApplyOpaqueMaterial()
{
var meshrenderers = prefab.GetComponentsInChildren<MeshRenderer>();
List<Material> mats = new();
foreach (var meshRenderer in meshrenderers)
{
foreach (var mat in meshRenderer.sharedMaterials)
{
mats.Add(mat);
}
}
renderInfo.opaqueMaterial = mats;
}
protected void CreateTransparentMaterialAndApply()
{
if (prefab == null)
return;
var shader = AssetDatabase.LoadAssetAtPath<Shader>(SCENE_TRANSPARENT_SHADER);
var mats = MaterialUtil.CreateMaterialWithShaderOnGameObject(prefab, shader);
if (mats == null)
return;
foreach (var mat in mats)
{
mat.enableInstancing = true;
EditorUtil.CreateAnUniqueAsset(mat, $"{SCENE_BRUSH_GENERATED_MATERIAL_PATH}/{name}_transparent.mat");
}
renderInfo.transparentMaterial = mats;
}
protected void ApplyMesh()
{
if (prefab == null)
return;
var meshFilters = prefab.GetComponentsInChildren<MeshFilter>();
List<Mesh> result = new();
foreach (var mf in meshFilters)
{
result.Add(mf.sharedMesh);
}
renderInfo.meshes = result;
}
protected virtual void OnMouseDownInScene(Vector3 mouseWorldPosition)
{
if (!isEraser)
DrawStandard(mouseWorldPosition);
else
{
EraseStandard(mouseWorldPosition);
}
_lastDrawPosition = mouseWorldPosition;
}
protected virtual void OnMouseDragInScene(Vector3 mouseWorldPosition, float dragDistance)
{
if (dragDistance >= radius)
{
if (!isEraser)
DrawStandard(mouseWorldPosition);
else
EraseStandard(mouseWorldPosition);
_lastDrawPosition = mouseWorldPosition;
}
}
protected virtual void OnMouseUpInScene(Vector3 mouseWorldPosition)
{
}
public virtual void OnDelete()
{
if (Valid)
{
foreach (var mat in renderInfo.transparentMaterial)
{
if (AssetDatabase.IsSubAsset(mat))
{
AssetDatabase.RemoveObjectFromAsset(mat);
}
else
{
//Object.DestroyImmediate(mat, true);
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(mat));
}
}
}
var path = AssetDatabase.GetAssetPath(renderInfo);
//DestroyImmediate(renderInfo, true);
AssetDatabase.DeleteAsset(path);
}
#endregion
#region Other
protected void DrawStandard(Vector3 position)
{
var locations = StandardDrawCalculate(position);
if (locations != null)
{
foreach (var location in locations)
{
DrawASceneObject(location.position, location.rotation);
}
}
}
protected void EraseStandard(Vector3 position)
{
var sObjs = StandardEraseCalculate(position);
foreach (var sObj in sObjs)
{
EraseASceneObject(sObj);
}
}
protected void DrawASceneObject(Vector3 position, Quaternion rotation)
{
var instance = PrefabUtility.InstantiatePrefab(prefab, GetTypeRoot()) as GameObject;
if (instance != null)
{
instance.transform.position = position;
instance.transform.rotation = rotation;
instance.transform.localScale *= scale;
var sceneObj = instance.AddComponent<SceneObject>();
sceneObj.renderInfo = renderInfo;
sceneObj.sceneObjectType = SceneObjectType;
Undo.RegisterCompleteObjectUndo(_sceneManager, BRUSH_DRAW_UNDO_NAME);
_sceneManager.sceneObjects.Add(sceneObj);
Undo.RegisterCreatedObjectUndo(instance, BRUSH_DRAW_UNDO_NAME);
EditorSceneManager.MarkSceneDirty(instance.scene);
EditorUtility.SetDirty(_sceneManager);
}
}
protected void EraseASceneObject(SceneObject sceneObject)
{
if (sceneObject != null)
{
Undo.RegisterCompleteObjectUndo(_sceneManager, BRUSH_DRAW_UNDO_NAME);
_sceneManager.sceneObjects.Remove(sceneObject);
Undo.DestroyObjectImmediate(sceneObject.gameObject);
EditorUtility.SetDirty(_sceneManager);
}
}
private Transform GetTypeRoot()
{
var trans = _sceneManager.Art.Find(RootName);
if (trans == null)
{
GameObject go = new GameObject();
go.name = RootName;
go.transform.parent = _sceneManager.Art;
trans = go.transform;
}
return trans;
}
#endregion
}