kingBook

导航

unity 在模型上绘画

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)。

posted on 2020-02-08 16:31  kingBook  阅读(1019)  评论(0编辑  收藏  举报