Tanoda
MengerSponge.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using UnityEngine;
10using System.Collections.Generic;
11
13
14 [ExecuteInEditMode]
15 public class MengerSponge : MonoBehaviour {
16
17 #pragma warning disable 0649
18 [SerializeField]
19 private int _rendererLod = 2;
20
21 [SerializeField]
22 private int _subMeshLod = 3;
23
24 [SerializeField]
25 private Material _material;
26
27 [SerializeField]
28 private bool _overrideShadowDistance = false;
29 #pragma warning restore 0649
30
31 private List<GameObject> _renderers = new List<GameObject>();
32 private Mesh _subMesh = null;
33
34 void OnValidate() {
35 _rendererLod = Mathf.Clamp(_rendererLod, 1, 3);
36 _subMeshLod = Mathf.Clamp(_subMeshLod, 1, 3);
37 _subMesh = null;
38 }
39
40 void Update() {
41 if (_overrideShadowDistance && Application.isPlaying) {
42 QualitySettings.shadowDistance = transform.lossyScale.x * 10;
43 }
44
45 if (_subMesh == null) {
46 for (int i = 0; i < _renderers.Count; i++) {
47 DestroyImmediate(_renderers[i]);
48 }
49 _renderers.Clear();
50
51 _subMesh = generateMengerMesh(_subMeshLod);
52
53 int size = Mathf.RoundToInt(Mathf.Pow(3, _rendererLod));
54 for (int x = 0; x < size; x++) {
55 for (int y = 0; y < size; y++) {
56 for (int z = 0; z < size; z++) {
57 if (isSpaceFilled(x, y, z, size / 3)) {
58 GameObject subRenderer = new GameObject("MengerPiece");
59 subRenderer.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
60 subRenderer.transform.parent = transform;
61 subRenderer.transform.localPosition = new Vector3(x, y, z) / size + Vector3.one * 0.5f / size - Vector3.one * 0.5f;
62 subRenderer.transform.localRotation = Quaternion.identity;
63 subRenderer.transform.localScale = Vector3.one / size;
64
65 subRenderer.AddComponent<MeshFilter>().mesh = _subMesh;
66 subRenderer.AddComponent<MeshRenderer>().sharedMaterial = _material;
67 _renderers.Add(subRenderer);
68 }
69 }
70 }
71 }
72 }
73 }
74
75 private Mesh generateMengerMesh(int lod) {
76 Mesh mesh = new Mesh();
77 mesh.name = "MengerMesh";
78
79 int size = Mathf.RoundToInt(Mathf.Pow(3, _subMeshLod));
80
81 bool[,,] _isFilled = new bool[size, size, size];
82 for (int x = 0; x < size; x++) {
83 for (int y = 0; y < size; y++) {
84 for (int z = 0; z < size; z++) {
85 if (isSpaceFilled(x, y, z, size / 3)) {
86 _isFilled[x, y, z] = true;
87 }
88 }
89 }
90 }
91
92 List<Vector3> verts = new List<Vector3>();
93 List<int> tris = new List<int>();
94
95 float quadRadius = 0.5f / size;
96
97 for (int x = 0; x < size; x++) {
98 for (int y = 0; y < size; y++) {
99 for (int z = 0; z < size; z++) {
100 if (_isFilled[x, y, z]) {
101 Vector3 position = new Vector3(x, y, z) / size + Vector3.one * quadRadius - Vector3.one * 0.5f;
102
103 if (x == 0 || !_isFilled[x - 1, y, z]) {
104 addQuad(verts, tris, position + Vector3.left * quadRadius, Vector3.forward, Vector3.up, quadRadius);
105 }
106 if (x == size - 1 || !_isFilled[x + 1, y, z]) {
107 addQuad(verts, tris, position + Vector3.right * quadRadius, Vector3.up, Vector3.forward, quadRadius);
108 }
109
110 if (y == 0 || !_isFilled[x, y - 1, z]) {
111 addQuad(verts, tris, position + Vector3.down * quadRadius, Vector3.right, Vector3.forward, quadRadius);
112 }
113 if (y == size - 1 || !_isFilled[x, y + 1, z]) {
114 addQuad(verts, tris, position + Vector3.up * quadRadius, Vector3.forward, Vector3.right, quadRadius);
115 }
116
117 if (z == 0 || !_isFilled[x, y, z - 1]) {
118 addQuad(verts, tris, position + Vector3.back * quadRadius, Vector3.up, Vector3.right, quadRadius);
119 }
120 if (z == size - 1 || !_isFilled[x, y, z + 1]) {
121 addQuad(verts, tris, position + Vector3.forward * quadRadius, Vector3.right, Vector3.up, quadRadius);
122 }
123 }
124 }
125 }
126 }
127
128 List<Vector2> uvs = new List<Vector2>();
129 for (int i = 0; i < verts.Count; i++) {
130 uvs.Add(verts[i]);
131 }
132
133 mesh.SetVertices(verts);
134 mesh.SetUVs(0, uvs);
135 mesh.SetIndices(tris.ToArray(), MeshTopology.Triangles, 0);
136 mesh.RecalculateNormals();
137 mesh.RecalculateBounds();
138
139 mesh.UploadMeshData(true);
140
141 return mesh;
142 }
143
144 private void addQuad(List<Vector3> verts, List<int> tris, Vector3 center, Vector3 axisA, Vector3 axisB, float radius) {
145 tris.Add(verts.Count + 0);
146 tris.Add(verts.Count + 1);
147 tris.Add(verts.Count + 2);
148
149 tris.Add(verts.Count + 0);
150 tris.Add(verts.Count + 2);
151 tris.Add(verts.Count + 3);
152
153 verts.Add(center + axisA * radius + axisB * radius);
154 verts.Add(center - axisA * radius + axisB * radius);
155 verts.Add(center - axisA * radius - axisB * radius);
156 verts.Add(center + axisA * radius - axisB * radius);
157 }
158
159 private bool isSpaceFilled(int x, int y, int z, int size) {
160 if (size == 1) {
161 return spaceFilledBaseCase(x, y, z);
162 }
163
164 if (!spaceFilledBaseCase(x / size, y / size, z / size)) {
165 return false;
166 }
167
168 x -= (x / size) * size;
169 y -= (y / size) * size;
170 z -= (z / size) * size;
171
172 return isSpaceFilled(x, y, z, size / 3);
173 }
174
175 private bool spaceFilledBaseCase(int x, int y, int z) {
176 int ones = 0;
177 if (x == 1) ones++;
178 if (y == 1) ones++;
179 if (z == 1) ones++;
180 return ones < 2;
181 }
182 }
183}