1. 程式人生 > >基於shatter tookit外掛實現場景模型切割(附外掛及核心程式碼)

基於shatter tookit外掛實現場景模型切割(附外掛及核心程式碼)

前言

專案需求就是,把當前視野內的模型摳下來,然後資料匯出供後邊的模組使用,是一個三維模擬相關的專案。
專案結構比較簡單,主要分三個部分:
1.獲取視野內不需要切割的模型列表,挨個克隆並寫入結果集陣列
2.獲取視野邊界線上的需要切割的模型列表,按順序克隆切割並把視野內的部分寫入結果集陣列
3.按照指定格式匯出結果集陣列中的三維資料,基於OBJ匯出實現

獲取模型列表的方式

採用,射線碰撞檢測的方式,首先對視野邊界線也就是視點座標(0,0)、(0,1)、(1,1)、(1,0)四個連線線做射線檢測,射線的源即是相機本身,由攝像機發射的射線到邊界線某一點的射線,撞擊gameobject碰撞體,成功發生碰撞,即Physics.Raycast(ray, out hit)存在(射線ray的碰撞結果hit存在),則檢測到需要切割的模型。
射線的密度人工設定(raycastCount),我現在用的是每條連線線檢測十次。

// Find game objects to split by raycasting at points along the line
		for (int i = 0; i < raycastCount; i++) {
			Ray ray = mainCamera.ScreenPointToRay (Vector3.Lerp (start, end, (float)i / raycastCount));
			RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
					

以上是對單一邊界線所做的射線檢測,外面還要一個大迴圈,用於給每個邊界線做檢測。
最好保持檢測順序和切割順序一致,看起來也方便


通過這種方式便可獲得待切割模型列表。
然後對視野內進行同密度的射線檢測,若檢測到待切割模型列表的gameobject,則不存入待克隆模型資料集。如此就能獲得僅僅出現在視野內需要完整克隆的模型了。

如何切割模型

切割這部分,基本上只是對shatter tookit外掛進行了精簡,移除了我不需要的功能而已。
這裡先說一下,外掛的結構,以及工作流程,不然,我直接往下說,可能看不太懂。

外掛專案結構

在這裡插入圖片描述
在這裡插入圖片描述
Core的library都是基本的庫,不涉及功能,所以不展開。
兩個Main中的四個檔案,WorldUvMapper.cs、ShatterScheduler.cs必須掛給攝像機,
ShatterTool.cs是可以對不同gameobject設定不同切割、擊碎屬性的功能,我的專案中,目前統一設定,而且一開始就給全部gameobject新增此功能,著實有些消耗資源,所以我採用了動態新增的方式,即給需要切割的gameobject新增此元件。
TargetUvMapper.cs,在實際使用中,沒發現他的作用,也可能是用於我移除的功能,暫不考慮。
在Mouse和Scheduler資料夾下,所有名為spilt的檔案都是關於切割的,名為shatter的是關於擊碎的,就是點選某一點,擊碎gameobject(大概就是隨機分裂)。

外掛工作流程

示例專案中,所有gameobject均在視野範圍內,對所有gameobject均添加了shattertool.cs元件,若場景更大,可能會影響載入速度。
據此,我採用瞭如下方法,動態新增shattertool.cs:

//新增切割任務,併發送gameobject及切割面
	scheduler.AddTask (new SplitTask (hit.collider.gameObject, new Plane[] { splitPlane }));
//在對新的任務初始化時,動態新增shattertool.cs元件
	public SplitTask(GameObject gameObject, Plane[] planes)
	{
		this.gameObject = gameObject;
		if (this.gameObject.GetComponent<shatter_tool>()==null) {
			this.gameObject.AddComponent<shatter_tool>();
		}

滑鼠點選gameobject,是shatter也就是擊碎功能,相機會向滑鼠點選的視點座標發出射線,進行碰撞檢測,若射線碰撞到任一gameobject,則傳送一個shattertask任務給shatterscheduler.cs,由相機實時監測任務的產生,若出現新任務,立即執行。
滑鼠按下劃過gameobject然後擡起,是split也就是切割功能,相機想滑鼠劃過的路徑發出射線,進行碰撞檢測,射線碰撞到的物體便是待切割gameobject,併發送splittask任務給shatterscheduler.cs,由相機監控任務列表,執行新任務。

改動

由於我的需求是將整個視野場景“摳下來”,所以需要對視野四個邊界線上碰撞的模型進行切割,那麼就會存在一個問題。

	若視點座標(0,0)到(1,0)上檢測到gameobject A需要切割,在視點座標(1,1)到(0,1)同樣
檢測到A需要切割,那麼要得到我需要的部分,就需要對A進行兩次連續的切割。

	那麼問題來了,每次切割都是克隆後再切割,切割會產生新的gameobject,而檢測時,給任務列表傳送的
待切割gameobject是克隆前的原版,就不能進行兩次對同意gameobject的切割操作。

	為此我想了一個辦法,依舊是先檢測後切割,但是檢測到gameobject也不新增新任務,檢測完成後執行第一
次切割,每次切割都需要等待上一次切割完成,把上一次切割後視野內的部分留下並命名為“gameobject.nam+"_C"”,
給名字上加一個特殊字尾,意味著是經過切割的部分。
	執行完一次切割,然後對下一次切割的待切割模型列表進行遍歷,若發現名為“gameobject.name+"_C"”的
gameobject,就給帶字尾的gameobject名字寫入新任務,再進行下一次切割。
	採用協程的方式,等待上一次切割完畢再繼續執行切割操作,等待時間需要調調參,基本完成了我的需求。
 for (int i = 0; i < 4; i++) {//四個邊界,切割四次
            for (int j = 0; j < slice_model[i].Count; j++)//slice_model為待切割模型列表
            { 
                name = slice_model[i][j].ToString();
           
                if (!_obj.Contains(name))//結果集陣列
                    _obj.Add(name);
                if (i > 0)//若不是第一次切割
                {
                    if (GameObject.Find(name + "_C") != null)
                    {
                        name = name + "_C";
                    }
                }
				if (scheduler != null) {
					scheduler.AddTask (new SplitTask (GameObject.Find(name), new Plane[] { (Plane)splitPlane[i] }));
				} 
			}
			yield return new WaitForSeconds(0.1f);
		}

連結:外掛及核心程式碼——GitHub.