using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 在模型上绘画
/// </summary>
public class DrawOnModel:BaseMonoBehaviour{
public GameObject target;
[Tooltip("颜色")]
public Color color=Color.cyan;
[Tooltip("笔刷大小")]
public float brushSize=10f;
public Texture2D texture2D { get; private set; }
private Camera m_mainCamera;
private struct PointColor{
public PointColor(int x,int y,Color cor){
point=new Vector2Int(x,y);
color=cor;
}
public Vector2Int point;
public Color color;
}
private List<PointColor[]> m_undoList=new List<PointColor[]>();
private List<PointColor> m_tempUndoList;
private RaycastHit[] m_raycastHits=new RaycastHit[1];
protected override void Awake(){
base.Awake();
m_mainCamera=Camera.main;
Renderer renderer=target.GetComponent<Renderer>();
Texture2D mainTexture=(Texture2D)renderer.material.mainTexture;
//创建一个副本,避免修改原纹理
texture2D=new Texture2D(mainTexture.width,mainTexture.height);
texture2D.SetPixels(mainTexture.GetPixels());
texture2D.Apply();
renderer.material.mainTexture=texture2D;
}
protected override void Update2(){
base.Update2();
if(Input.touchSupported){
if(Input.touchCount>0){
Touch touch=Input.GetTouch(0);
if(touch.phase==TouchPhase.Began){
DrawBegin(touch.position);
}else if(touch.phase==TouchPhase.Moved){
if(!EventSystem.current.IsPointerOverGameObject(touch.fingerId)){
DrawScreenPoint(touch.position);
}
}else if(touch.phase==TouchPhase.Ended||touch.phase==TouchPhase.Canceled){
DrawEnd();
}
}
}else{
Vector2 mousePos=Input.mousePosition;
if(Input.GetMouseButtonDown(0)){
DrawBegin(mousePos);
}
if(Input.GetMouseButton(0)&&!EventSystem.current.IsPointerOverGameObject()){
DrawScreenPoint(mousePos);
}
if(Input.GetMouseButtonUp(0)){
DrawEnd();
}
}
}
private void DrawBegin(Vector2 screenPos){
m_tempUndoList=new List<PointColor>();
}
private void DrawScreenPoint(Vector2 screenPos){
Physics.RaycastNonAlloc(m_mainCamera.ScreenPointToRay(screenPos),m_raycastHits);
var hit=m_raycastHits[0];
if(hit.collider!=null&&hit.collider.gameObject==target){
PointColor[] undoList=DrawCircleDot(hit.textureCoord,texture2D,color,brushSize);
if(undoList!=null&&undoList.Length>0){
m_tempUndoList.AddRange(undoList);
}
}
}
private void DrawEnd(){
if(m_tempUndoList!=null&&m_tempUndoList.Count>0){
m_undoList.Add(m_tempUndoList.ToArray());
m_tempUndoList=null;
}
}
/// <summary>
/// 画圆形点
/// </summary>
/// <param name="point">坐标</param>
/// <param name="texture">贴图</param>
/// <param name="color">颜色</param>
/// <param name="diameter">直径</param>
/// <returns>返回改变的像素的旧颜色列表</returns>
private PointColor[] DrawCircleDot(Vector2 point,Texture2D texture,Color color,float diameter){
float radius=diameter*0.5f;
point.x*=texture.width;
point.y*=texture.height;
point.x-=radius;
point.y-=radius;
int x=Mathf.FloorToInt(point.x);
int y=Mathf.FloorToInt(point.y);
List<PointColor> tempList=new List<PointColor>();
for (int i=0;i<diameter;i++){
for (int j=0;j<diameter;j++){
float dx=i-radius;
float dy=j-radius;
float distance=Mathf.Sqrt(dx*dx+dy*dy);
if(distance<=radius){
int px=x+i;
int py=y+j;
Color oldColor=texture.GetPixel(px,py);
if(oldColor!=color){
tempList.Add(new PointColor(px,py,oldColor));
texture.SetPixel(px,py,color);
}
}
}
}
texture.Apply();
return tempList.ToArray();
}
/// <summary>
/// 撤消
/// </summary>
public void Undo(){
if(m_undoList.Count>0){
PointColor[] list=m_undoList[m_undoList.Count-1];
int len=list.Length;
for(int i=0;i<len;i++){
PointColor element=list[i];
texture2D.SetPixel(element.point.x,element.point.y,element.color);
}
texture2D.Apply();
m_undoList.RemoveAt(m_undoList.Count-1);
}
}
}
注意:
1.调用Texture2D.SetPixel()和Texture2D.GetPixel()时,在纹理导入设置中,必须勾选Read/Write Enabled,纹理格式仅支持RGBA32,ARGB32,RGB24,Alpha8(动态新建的Texture2D不必处理)。
2.调用RaycastHit.textureCoord时,如果目标网格在.fbx内,则.fbx文件的导入设置中,必须勾选Read/Write Enabled(未勾选在安卓中测试会出现秒退或始终返回0)。