Skip to content

Instantly share code, notes, and snippets.

@TSUMIKISEISAKU
Last active September 27, 2023 01:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TSUMIKISEISAKU/1c44d3b3ba8ea7720e7e70cb3dccc1f8 to your computer and use it in GitHub Desktop.
Save TSUMIKISEISAKU/1c44d3b3ba8ea7720e7e70cb3dccc1f8 to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
/// <summary>
/// マーキング操作の制御
/// </summary>
public class MarkingHandler : MonoBehaviour
{
[SerializeField, Tooltip("マーキングのComputeShader")]
private ComputeShader _computeShader;
[SerializeField, Tooltip("マーキング位置のガイド テスト用")]
private Transform _markingPosition;
[SerializeField, Tooltip("描画可能な線からの距離の閾値")]
private float _drawDistance = 0.2f;
[SerializeField, Tooltip("マーキングのMeshColliderのレイヤー")]
private LayerMask _layerMask;
[SerializeField, Tooltip("描画用RenderTextureのサイズ")]
private int _drawRenderTextureSize = 256;
[SerializeField, Tooltip("終了判定用RenderTextureのサイズ")]
private int _checkRenderTextureSize = 32;
[SerializeField, Tooltip("テクスチャ上の描画半径")]
private float _paintRadius = 0.05f;
// マーキング対象のデータセット
[System.Serializable]
private struct MarkingTarget
{
[Tooltip("マーキングの描画位置判定")]
public MeshCollider _markingMeshCollider;
[Tooltip("描画判定のマスク用テクスチャ")]
public Texture2D _maskTexture;
[Tooltip("マーキングの線のメッシュ")]
public MeshRenderer _markingMesh;
}
[SerializeField, Tooltip("マーキング対象のデータセット")]
private MarkingTarget[] _markingTargets;
// マーキングの線のマテリアル
private List<Material> _markingMaterials = new List<Material>();
// 読み書きそれぞれのRenderTextureを格納
[System.Serializable]
private struct RenderTexturePair
{
public RenderTexture _write;
public RenderTexture _read;
}
// マーキングの描画用RenderTexture
private List<RenderTexturePair> _drawTextures;
// マーキングの終了判定用RenderTexture
private List<RenderTexturePair> _checkTextures;
// マーキングの処理に付随するデータ管理
private KernelParamsHandler _markingKernel;
// 終了判定のカーネル
private KernelParamsHandler _checkKernel;
// 塗の進捗をComputeShaderから取得
private ComputeBuffer _progress;
// 塗の進捗
private float[] _paintProgress = new float[] {0};
// マーキング用テクスチャを塗るカーネルの名称
private string _paintKernelName = "Paint";
// 終了判定のカーネルの名称
private string _checkKernelName = "CheckProgress";
// スレッドグループのスレッドのサイズ
private const int SIMULATION_BLOCK_SIZE = 8;
// 入力位置の名称
private string _paintUVName = "_paintUV";
// 塗る範囲の半径
private string _paintRadiusName = "_paintRadius";
// 各種テクスチャの名称
private string _drawTextureWriteName = "_drawTexture_Write";
private string _drawTextureReadName = "_drawTexture_Read";
private string _checkTextureNameWrite = "_checkTexture_Write";
private string _checkTextureNameRead = "_checkTexture_Read";
private string _maskTextureName = "_maskTexture";
// 各種パラメータの名称
private string _drawTexResolutionName = "_drawTexResolution";
private string _checkTexResolutionName = "_checkTexResolution";
private string _progressName = "_progress";
private string _drawTextureProperty = "_drawTexture";
private string _sculptedAreaMapProperty = "_SculptedAreaMap";
[SerializeField, Tooltip("進捗確認用テキスト デバッグ用")]
private TextMesh _progressTextMesh;
[SerializeField, Tooltip("終了判定のテクスチャのデバッグ用")]
private Material _textureDebugMat;
// マーキング対象オブジェクトのIndex
private int _markingTargetId;
[SerializeField, Tooltip("マーキング操作をスキップするための白テクスチャ")]
private Texture2D whiteTex;
// Rayの交差判定を複数本分まとめて管理
private List<RaycastHit> _hitsAll = new List<RaycastHit>();
// Rayのオフセット方向
private Vector3[] _rayOffsetDirs = { Vector3.zero, Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
// Rayのオフセット距離
private float _rayOffsetDist = 0.005f;
[SerializeField, Tooltip("スカルプト範囲のテクスチャの取得")]
private SculptManager _sculptManager;
// スカルプト範囲のテクスチャ
private Texture _sculptedAreaMap;
/// <summary>
/// 初期化
/// </summary>
private void Start()
{
_markingTargetId = 0;
InitKernel();
InitRenderTextures();
InitMaskTexture(_markingTargetId);
InitParams();
}
/// <summary>
/// バッファの破棄
/// </summary>
private void OnDestroy()
{
_progress.Dispose();
}
/// <summary>
/// カーネルの初期化
/// </summary>
private void InitKernel()
{
int threadGroupSize = Mathf.CeilToInt(_drawRenderTextureSize / (float)SIMULATION_BLOCK_SIZE);
_computeShader = Instantiate(_computeShader);
_markingKernel = new KernelParamsHandler(_computeShader, _paintKernelName, threadGroupSize, threadGroupSize, 1);
_checkKernel = new KernelParamsHandler(_computeShader, _checkKernelName, 1, 1, 1);
}
/// <summary>
/// RenderTextureの初期化
/// </summary>
private void InitRenderTextures()
{
// RenderTextureのListを初期化
_drawTextures = new List<RenderTexturePair>();
_checkTextures = new List<RenderTexturePair>();
// テクスチャを設定
for (int i = 0; i < _markingTargets.Length; i++)
{
// 描画用・書き込み用
RenderTexture drawTexture1 = new RenderTexture(_drawRenderTextureSize, _drawRenderTextureSize, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
drawTexture1.enableRandomWrite = true;
drawTexture1.Create();
RenderTexture.active = drawTexture1;
GL.Clear(true, true, Color.black);
RenderTexture.active = null;
// 描画用・読み取り用
RenderTexture drawTexture2 = new RenderTexture(drawTexture1);
drawTexture2.enableRandomWrite = true;
drawTexture2.Create();
RenderTexture.active = drawTexture2;
GL.Clear(true, true, Color.black);
RenderTexture.active = null;
// 描画用 格納
RenderTexturePair drawPair = new RenderTexturePair();
drawPair._write = drawTexture1;
drawPair._read = drawTexture2;
_drawTextures.Add(drawPair);
// 判定用・書き込み用
RenderTexture checkTexture1 = new RenderTexture(_checkRenderTextureSize, _checkRenderTextureSize, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
checkTexture1.enableRandomWrite = true;
checkTexture1.Create();
RenderTexture.active = checkTexture1;
GL.Clear(true, true, Color.black);
RenderTexture.active = null;
// 判定用・読み取り用
RenderTexture checkTexture2 = new RenderTexture(checkTexture1);
checkTexture2.enableRandomWrite = true;
checkTexture2.Create();
RenderTexture.active = checkTexture2;
GL.Clear(true, true, Color.black);
RenderTexture.active = null;
// 判定用 格納
RenderTexturePair checkPair = new RenderTexturePair();
checkPair._write = checkTexture1;
checkPair._read = checkTexture2;
_checkTextures.Add(checkPair);
}
SetRenderTextures(_markingTargetId);
}
/// <summary>
/// 各種変数等の初期化
/// </summary>
private void InitParams()
{
// パラメータを設定
_computeShader.SetInt(_drawTexResolutionName, _drawTextures[_markingTargetId]._write.width);
_computeShader.SetInt(_checkTexResolutionName, _checkTextures[_markingTargetId]._write.width);
_computeShader.SetFloat(_paintRadiusName, _paintRadius);
if (_sculptManager)
{
_sculptedAreaMap = _sculptManager.GetUVOffsetMap();
}
for (int i = 0; i < _markingTargets.Length; i++)
{
Material mat = _markingTargets[i]._markingMesh.material;
_markingMaterials.Add(mat);
mat.SetTexture(_drawTextureProperty, _drawTextures[i]._write);
if(_sculptManager && _sculptedAreaMap)
{
mat.SetTexture(_sculptedAreaMapProperty, _sculptedAreaMap);
}
}
_progress = new ComputeBuffer(1, Marshal.SizeOf(typeof(float)));
float[] data = { 0 };
_progress.SetData(data);
_computeShader.SetBuffer(_checkKernel._index, _progressName, _progress);
}
/// <summary>
/// マーキング対象の設定
/// </summary>
/// <param name="index"></param>
public void SetMarkingTarget(int index)
{
_markingTargetId = index;
InitMaskTexture(_markingTargetId);
SetRenderTextures(_markingTargetId);
}
/// <summary>
/// マスク用テクスチャの初期設定
/// </summary>
private void InitMaskTexture(int index)
{
if(index < 0 || index >= _markingTargets.Length)
{
return;
}
// マスク用のテクスチャサイズは終了判定用テクスチャサイズに合わせる
if (_markingTargets[index]._maskTexture.width != _checkRenderTextureSize || _markingTargets[index]._maskTexture.height != _checkRenderTextureSize)
{
Texture2D checkMask = new Texture2D(_checkRenderTextureSize, _checkRenderTextureSize);
Graphics.ConvertTexture(_markingTargets[index]._maskTexture, checkMask);
_computeShader.SetTexture(_checkKernel._index, _maskTextureName, checkMask);
}
else
{
_computeShader.SetTexture(_checkKernel._index, _maskTextureName, _markingTargets[index]._maskTexture);
}
}
/// <summary>
/// 処理に使用するRenderTexureの差し替え
/// </summary>
/// <param name="index"></param>
private void SetRenderTextures(int index)
{
_computeShader.SetTexture(_markingKernel._index, _drawTextureWriteName, _drawTextures[index]._write);
_computeShader.SetTexture(_markingKernel._index, _drawTextureReadName, _drawTextures[index]._read);
_computeShader.SetTexture(_markingKernel._index, _checkTextureNameWrite, _checkTextures[index]._write);
_computeShader.SetTexture(_markingKernel._index, _checkTextureNameRead, _checkTextures[index]._read);
_computeShader.SetTexture(_checkKernel._index, _checkTextureNameRead, _checkTextures[index]._read);
// debugging
if(_textureDebugMat)
{
_textureDebugMat.SetTexture("_MainTex", _checkTextures[index]._write);
}
}
/// <summary>
/// 線のメッシュの表示・非表示
/// </summary>
/// <param name="index"></param>
/// <param name="toEnable"></param>
public void DisplayLine(int index, bool toEnable)
{
_markingTargets[index]._markingMesh.enabled = toEnable;
}
/// <summary>
/// 線のメッシュを全て非表示
/// </summary>
public void HideAllLines()
{
foreach(MarkingTarget target in _markingTargets)
{
target._markingMesh.enabled = false;
}
}
/// <summary>
/// マーカーを塗るテスト用関数
/// </summary>
/// <param name="position"></param>
public void PaintTest(Vector3 position, Vector3 direction)
{
// 描画位置付近のColliderを取得
_hitsAll.Clear();
foreach(Vector3 offsetDir in _rayOffsetDirs)
{
RaycastHit[] hits = Physics.RaycastAll(position + offsetDir * _rayOffsetDist, direction, _drawDistance, _layerMask);
_hitsAll.AddRange(hits);
}
// マーキングの線が検出されたら描画を実行
foreach (RaycastHit hit in _hitsAll)
{
if (hit.collider == _markingTargets[_markingTargetId]._markingMeshCollider)
{
_computeShader.SetVector(_paintUVName, hit.textureCoord);
_computeShader.Dispatch(_markingKernel._index, _markingKernel._x, _markingKernel._y, _markingKernel._z);
Graphics.CopyTexture(_drawTextures[_markingTargetId]._write, _drawTextures[_markingTargetId]._read);
Graphics.CopyTexture(_checkTextures[_markingTargetId]._write, _checkTextures[_markingTargetId]._read);
break;
}
}
}
/// <summary>
/// 塗の進捗をチェックするテスト用関数
/// </summary>
/// <returns></returns>
public float CheckProgress()
{
_computeShader.Dispatch(_checkKernel._index, _checkKernel._x, _checkKernel._y, _checkKernel._z);
_progress.GetData(_paintProgress);
return _paintProgress[0];
}
/// <summary>
/// 線を自動で全て塗り終わった状態にする
/// スキップ用
/// </summary>
public void PaintAll()
{
if(!whiteTex)
{
return;
}
_markingMaterials[_markingTargetId].SetTexture(_drawTextureProperty, whiteTex);
}
/// <summary>
/// デバッグ用
/// </summary>
private void Update()
{
// もしScluptedAreaMapの参照が未取得であれば取得
if (_sculptManager && _sculptedAreaMap == null)
{
_sculptedAreaMap = _sculptManager.GetUVOffsetMap();
for (int i = 0; i < _markingMaterials.Count; i++)
{
_markingMaterials[i].SetTexture(_sculptedAreaMapProperty, _sculptedAreaMap);
}
}
// デバッグ用になぞり具合の進捗を表示
if (_progressTextMesh)
{
float value = CheckProgress() * 100f;
_progressTextMesh.text = string.Format("{0:f2}", value) + "%";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment