Last active
June 11, 2017 10:36
-
-
Save onotchi/33ccd019a6eb5d1962406aded0d6c2d5 to your computer and use it in GitHub Desktop.
指定範囲のモデルをボクセルで生成し直す
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
namespace Onoty3D.VoxelGenerator.Scripts | |
{ | |
[Serializable] | |
public class InsideChecker | |
{ | |
//生成したボクセルに設定するレイヤー | |
public LayerMask TargetLayer; | |
//判定時に使うRayの距離 | |
public float RayDistance = 1f; | |
//Rayの太さ | |
public float RayRadius = 0.01f; | |
//視覚化 | |
[NonSerialized] | |
public bool Visualization = false; | |
//Rayを飛ばす方向 | |
private static readonly Vector3[] DIRECTIONS = new Vector3[] | |
{ | |
Vector3.right, | |
Vector3.left, | |
Vector3.up, | |
Vector3.down, | |
Vector3.forward, | |
Vector3.back, | |
}; | |
private Ray _rayBuff = default(Ray); | |
private RaycastHit _hitBuff; | |
private Dictionary<int, int[]> _trianglesDic = new Dictionary<int, int[]>(); | |
private Dictionary<int, List<int[]>> _subMeshTrianglesDic = new Dictionary<int, List<int[]>>(); | |
/// <summary> | |
/// 対象位置がメッシュに含まれるかの判定 | |
/// </summary> | |
/// <param name="point">対象位置</param> | |
/// <param name="color">含まれた場合、</param> | |
/// <returns></returns> | |
public bool IsInside(Vector3 point, out Color color) | |
{ | |
color = Color.black; | |
var distance = 0f; | |
var hit = default(RaycastHit); | |
//上下左右前後からRayを飛ばして、すべてメッシュに当たれば、対象のpositionは | |
//メッシュの内側にあると判断する | |
foreach (var direction in InsideChecker.DIRECTIONS) | |
{ | |
if (this.IsHit(point, direction)) | |
{ | |
if (this._hitBuff.distance > distance) | |
{ | |
//一番直近のhit情報を確保 | |
hit = this._hitBuff; | |
distance = hit.distance; | |
} | |
continue; | |
} | |
//一つでもhitしなければ、内側ではないと判断 | |
return false; | |
} | |
//内側と判定された時、色情報を取得 | |
var meshCollider = hit.transform.GetComponentInChildren<MeshCollider>(); | |
var renderer = hit.transform.GetComponentInChildren<Renderer>(); | |
if (renderer != null) | |
{ | |
//未知のメッシュの場合、ポリゴン情報を取得 | |
var id = meshCollider.GetInstanceID(); | |
if (!this._trianglesDic.ContainsKey(id)) | |
{ | |
this.SetTriangleInfo(id, meshCollider); | |
} | |
//マテリアルの特定と色の取得 | |
var material = renderer.materials[this.GetSubMeshIndex(id, hit.triangleIndex)]; | |
var textrue = material.mainTexture as Texture2D; | |
if (textrue == null) | |
{ | |
if (material.HasProperty("_Color")) | |
{ | |
color = material.color; | |
} | |
} | |
else | |
{ | |
var uv = hit.textureCoord; | |
uv.x *= textrue.width; | |
uv.y *= textrue.height; | |
color = textrue.GetPixel((int)uv.x, (int)uv.y); | |
} | |
} | |
return true; | |
} | |
private void SetTriangleInfo(int id, MeshCollider meshCollider) | |
{ | |
this._trianglesDic[id] = meshCollider.sharedMesh.triangles; | |
var list = this._subMeshTrianglesDic[id] = new List<int[]>(meshCollider.sharedMesh.subMeshCount); | |
for (int i = 0; i < meshCollider.sharedMesh.subMeshCount; i++) | |
{ | |
list.Add(meshCollider.sharedMesh.GetTriangles(i)); | |
} | |
} | |
private bool IsHit(Vector3 point, Vector3 direction) | |
{ | |
this._rayBuff.origin = point - direction * this.RayDistance; | |
this._rayBuff.direction = direction; | |
if (this.Visualization) | |
{ | |
Debug.DrawRay(this._rayBuff.origin, this._rayBuff.direction * this.RayDistance, Color.red, 0.1f, false); | |
} | |
//if (Physics.Raycast(this._ray, out this._hit, Distance)) | |
if (Physics.SphereCast(this._rayBuff, this.RayRadius, out this._hitBuff, this.RayDistance, this.TargetLayer)) | |
{ | |
return this._hitBuff.collider is MeshCollider; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
private int GetSubMeshIndex(int id, int hitTriangleIndex) | |
{ | |
if (this._subMeshTrianglesDic[id].Count == 1) | |
{ | |
return 0; | |
} | |
var hitTriangle = new int[] | |
{ | |
this._trianglesDic[id][hitTriangleIndex * 3], | |
this._trianglesDic[id][hitTriangleIndex * 3 + 1], | |
this._trianglesDic[id][hitTriangleIndex * 3 + 2] | |
}; | |
for (int i = 0; i < this._subMeshTrianglesDic[id].Count; i++) | |
{ | |
var subMeshTriangles = this._subMeshTrianglesDic[id][i]; | |
for (int j = 0; j < subMeshTriangles.Length; j += 3) | |
{ | |
if (subMeshTriangles[j] == hitTriangle[0] | |
&& subMeshTriangles[j + 1] == hitTriangle[1] | |
&& subMeshTriangles[j + 2] == hitTriangle[2]) | |
{ | |
return i; | |
} | |
} | |
} | |
return 0; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Onoty3D.VoxelGenerator.Scripts | |
{ | |
[Serializable] | |
public class VoxelCount | |
{ | |
public int X = 10; | |
public int Y = 10; | |
public int Z = 10; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
namespace Onoty3D.VoxelGenerator.Scripts | |
{ | |
public class VoxelGenerator : MonoBehaviour | |
{ | |
//ボクセル判定範囲 | |
public VoxelTargetArea TargetArea; | |
//内外判定クラスインスタンス | |
public InsideChecker InsideChecker; | |
//生成したボクセルに設定するレイヤー | |
public LayerMask VoxelLayer; | |
//生成したボクセルの親 | |
public Transform Parent; | |
//ボクセルに付加するマテリアルのベース | |
public Material BaseMaterial; | |
//ボクセルに付加するマテリアルのシェーダーの色割当パラメータ | |
public string MaterialColorName = "_Color"; | |
//視覚化 | |
public bool Visualization = false; | |
//private InsideChecker _insideChecker; | |
private Dictionary<Color, Material> _materialDic = new Dictionary<Color, Material>(); | |
private Transform _transform; | |
//ボクセルひとつのサイズ | |
private Vector3 _voxelScale; | |
//ボクセル生成エリアの始点 | |
private Vector3 _startPosition; | |
//ボクセル判定場所 | |
private Vector3 _targetPosition; | |
//ボクセル生成場所 | |
private Vector3 _voxelPosition; | |
//ボクセルの色 | |
private Color _voxelColor; | |
public void Generate() | |
{ | |
this.Initialize(); | |
if (this.Visualization) | |
{ | |
StartCoroutine(GenerateCoreVisualization()); | |
} | |
else | |
{ | |
this.GenerateCore(); | |
} | |
} | |
// Use this for initialization | |
private void Start() | |
{ | |
this._transform = this.transform; | |
} | |
// Update is called once per frame | |
private void Update() | |
{ | |
} | |
private void OnDrawGizmos() | |
{ | |
if (this.TargetArea == null) | |
{ | |
return; | |
} | |
Gizmos.color = Color.red; | |
Gizmos.DrawWireCube(this.transform.position, this.TargetArea.transform.localScale); | |
} | |
private void Initialize() | |
{ | |
this._voxelScale = this.TargetArea.GetVoxelScale(); | |
this._startPosition = this.TargetArea.GetStartPosion(); | |
this._targetPosition = this.TargetArea.transform.position; | |
this._voxelPosition = Vector3.zero; | |
this._voxelColor = Color.black; | |
this.InsideChecker.Visualization = this.Visualization; | |
} | |
private void GenerateCore() | |
{ | |
//ぐるぐる回してボクセル生成 | |
//下から積み上げたいのでY→X→Z順でインクリメント | |
for (int y = 0; y < this.TargetArea.VoxelCount.Y; y++) | |
{ | |
this._voxelPosition.y = this._startPosition.y + this._voxelScale.y * y; | |
for (int x = 0; x < this.TargetArea.VoxelCount.X; x++) | |
{ | |
this._voxelPosition.x = this._startPosition.x + this._voxelScale.x * x; | |
for (int z = 0; z < this.TargetArea.VoxelCount.Z; z++) | |
{ | |
this._voxelPosition.z = this._startPosition.z + this._voxelScale.z * z; | |
if (this.InsideChecker.IsInside(this._targetPosition + this._voxelPosition, out this._voxelColor)) | |
{ | |
this.SpawnVoxel(); | |
} | |
} | |
} | |
} | |
} | |
private IEnumerator GenerateCoreVisualization() | |
{ | |
WaitForSeconds wait = new WaitForSeconds(0.01f); | |
//ぐるぐる回してボクセル生成 | |
//下から積み上げたいのでY→X→Z順でインクリメント | |
for (int y = 0; y < this.TargetArea.VoxelCount.Y; y++) | |
{ | |
this._voxelPosition.y = this._startPosition.y + this._voxelScale.y * y; | |
for (int x = 0; x < this.TargetArea.VoxelCount.X; x++) | |
{ | |
this._voxelPosition.x = this._startPosition.x + this._voxelScale.x * x; | |
for (int z = 0; z < this.TargetArea.VoxelCount.Z; z++) | |
{ | |
this._voxelPosition.z = this._startPosition.z + this._voxelScale.z * z; | |
if (this.InsideChecker.IsInside(this._targetPosition + this._voxelPosition, out this._voxelColor)) | |
{ | |
this.SpawnVoxel(); | |
yield return wait; | |
} | |
} | |
} | |
} | |
} | |
private void SpawnVoxel() | |
{ | |
var voxel = GameObject.CreatePrimitive(PrimitiveType.Cube); | |
//GameObject.Destroy(voxel.GetComponent<BoxCollider>()); | |
voxel.layer = (int)Mathf.Log(this.VoxelLayer.value, 2); | |
voxel.transform.position = this._transform.position + this._voxelPosition; | |
voxel.transform.localScale = this._voxelScale; | |
//voxel.AddComponent<Rigidbody>(); | |
voxel.transform.SetParent(this.Parent); | |
//マテリアルセット | |
//同色のものは共通して使う | |
if (!this._materialDic.ContainsKey(this._voxelColor)) | |
{ | |
this._materialDic[this._voxelColor] = Instantiate(this.BaseMaterial); | |
this._materialDic[this._voxelColor].SetColor(this.MaterialColorName, this._voxelColor); | |
} | |
voxel.GetComponent<MeshRenderer>().material = this._materialDic[this._voxelColor]; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEditor; | |
using UnityEngine; | |
namespace Onoty3D.VoxelGenerator.Scripts.Editor | |
{ | |
[CustomEditor(typeof(VoxelGenerator))] | |
public class VoxelGeneratorEditor : UnityEditor.Editor | |
{ | |
public override void OnInspectorGUI() | |
{ | |
//元のInspector部分を表示 | |
base.OnInspectorGUI(); | |
if (GUILayout.Button("Generate")) | |
{ | |
if (Application.isPlaying) | |
{ | |
var generator = target as VoxelGenerator; | |
generator.Generate(); | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
using UnityEngine; | |
namespace Onoty3D.VoxelGenerator.Scripts | |
{ | |
public class VoxelTargetArea : MonoBehaviour | |
{ | |
public VoxelCount VoxelCount; | |
private Transform _transform; | |
public Vector3 GetVoxelScale() | |
{ | |
return new Vector3( | |
this._transform.localScale.x / (float)this.VoxelCount.X | |
, this._transform.localScale.y / (float)this.VoxelCount.Y | |
, this._transform.localScale.z / (float)this.VoxelCount.Z | |
); | |
} | |
public Vector3 GetStartPosion() | |
{ | |
return -this._transform.localScale / 2f + GetVoxelScale() / 2f; ; | |
} | |
// Use this for initialization | |
private void Start() | |
{ | |
this._transform = this.transform; | |
} | |
// Update is called once per frame | |
private void Update() | |
{ | |
} | |
private void OnDrawGizmos() | |
{ | |
if (this.VoxelCount == null) | |
{ | |
return; | |
} | |
var transform = this.transform; | |
Gizmos.color = Color.blue; | |
Gizmos.DrawWireCube(transform.position, transform.localScale); | |
//if (Application.isPlaying) | |
//{ | |
// return; | |
//} | |
var voxelScale = new Vector3( | |
transform.localScale.x / (float)this.VoxelCount.X | |
, transform.localScale.y / (float)this.VoxelCount.Y | |
, transform.localScale.z / (float)this.VoxelCount.Z | |
); | |
var startPosition = -this.transform.localScale / 2f + voxelScale / 2f; | |
var voxelPosition = Vector3.zero; | |
var scale = Vector3.one * 0.01f; | |
for (int x = 0; x < this.VoxelCount.X; x++) | |
{ | |
voxelPosition.x = startPosition.x + voxelScale.x * x; | |
for (int y = 0; y < this.VoxelCount.Y; y++) | |
{ | |
voxelPosition.y = startPosition.y + voxelScale.y * y; | |
for (int z = 0; z < this.VoxelCount.Z; z++) | |
{ | |
voxelPosition.z = startPosition.z + voxelScale.z * z; | |
if (x == 0 | |
|| y == 0 | |
|| z == 0 | |
|| x == this.VoxelCount.X - 1 | |
|| y == this.VoxelCount.Y - 1 | |
|| z == this.VoxelCount.Z - 1 | |
) | |
{ | |
Gizmos.DrawCube(transform.position + voxelPosition, scale); | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment