/
DebugPrimitiveRenderer.cs
490 lines (442 loc) · 13.4 KB
/
DebugPrimitiveRenderer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
using System.Collections.Generic;
using UnityEngine;
namespace Kayac
{
public abstract class DebugPrimitiveRenderer : System.IDisposable
{
public enum AlignX
{
Left,
Center,
Right,
}
public enum AlignY
{
Top,
Center,
Bottom,
}
public enum TextOverflow
{
Scale, // 箱に収めるようスケール
Wrap,
}
public const float DefaultLineSpacingRatio = 0f;
protected const int DefaultTriangleCapacity = 1024;
const int InitialSubMeshCapacity = 16;
Shader _textShader;
Shader _texturedShader;
Mesh _mesh;
MaterialPropertyBlock _materialPropertyBlock;
Material _textMaterial;
Material _texturedMaterial;
int _textureShaderPropertyId;
protected Font _font;
protected int _vertexCount;
protected int _capacity;
protected Vector2 _whiteUv;
protected Vector3[] _vertices;
protected int _indexCount;
protected Vector2[] _uv;
protected Color32[] _colors;
protected int[] _indices;
protected List<Vector2> _temporaryUv; // SetTriangles寸前に使う
protected List<Vector3> _temporaryVertices; // SetTriangles寸前に使う
protected List<Color32> _temporaryColors; // SetTriangles寸前に使う
protected List<int> _temporaryIndices; // SetTriangles寸前に使う
#if UNITY_WEBGL
const char _whiteChar = '.';
#else
const char _whiteChar = '■';
#endif
string _whiteString = new string(_whiteChar, 1);
class SubMesh
{
public void FixIndexCount(int indexPosition)
{
indexCount = indexPosition - indexStart;
}
public Material material;
public Texture texture;
public int indexStart;
public int indexCount;
}
List<SubMesh> _subMeshes;
// 毎フレーム0にリセットする。
int _subMeshCount;
Texture _texture;
MeshFilter _meshFilter;
MeshRenderer _meshRenderer;
public Color32 color { get; set; }
protected Texture fontTexture { get; private set; }
public DebugPrimitiveRenderer(
Shader textShader,
Shader texturedShader,
Font font,
MeshRenderer meshRenderer,
MeshFilter meshFilter,
int capacity = DefaultTriangleCapacity)
{
_textShader = textShader;
_texturedShader = texturedShader;
_font = font;
_meshRenderer = meshRenderer;
_meshFilter = meshFilter;
color = new Color32(255, 255, 255, 255);
_mesh = new Mesh();
_mesh.MarkDynamic();
_meshFilter.mesh = _mesh;
_textMaterial = new Material(_textShader);
_texturedMaterial = new Material(_texturedShader);
_materialPropertyBlock = new MaterialPropertyBlock();
_textureShaderPropertyId = Shader.PropertyToID("_MainTex");
Font.textureRebuilt += OnFontTextureRebuilt;
_font.RequestCharactersInTexture(_whiteString);
SetCapacity(capacity);
// 初回は手動
OnFontTextureRebuilt(_font);
}
public void SetCapacity(int triangleCapacity)
{
_capacity = triangleCapacity * 3;
if (_capacity >= 0xffff)
{
Debug.LogWarning("triangleCapacity must be < 0xffff/3. clamped.");
_capacity = 0xffff;
}
_vertices = new Vector3[_capacity];
_uv = new Vector2[_capacity];
_colors = new Color32[_capacity];
_indices = new int[_capacity];
_temporaryVertices = new List<Vector3>(_capacity); // SetTriangles寸前に使う
_temporaryColors = new List<Color32>(_capacity); // SetTriangles寸前に使う
_temporaryUv = new List<Vector2>(_capacity); // SetTriangles寸前に使う
_temporaryIndices = new List<int>(_capacity); // SetTriangles寸前に使う
_vertexCount = 0;
_indexCount = 0; // すぐ足すことになる
_subMeshes = new List<SubMesh>();
_subMeshes.Capacity = InitialSubMeshCapacity;
}
public void Dispose()
{
Font.textureRebuilt -= OnFontTextureRebuilt;
}
// 描画キックを行う
public void UpdateMesh()
{
// ■だけは常に入れておく。他は文字描画要求の都度投げる
_font.RequestCharactersInTexture(_whiteString);
// 描画キック
_mesh.Clear();
Material[] materials = null;
if (_subMeshCount > 0)
{
_subMeshes[_subMeshCount - 1].FixIndexCount(_indexCount);
// 使用量が半分以下の場合、テンポラリにコピーしてから渡す
if (_vertexCount < (_capacity / 2)) // 閾値は研究が必要だが、とりあえず。
{
UnityEngine.Profiling.Profiler.BeginSample("DebugPrimitiveRenderer.UpdateMesh.FillTemporary");
_temporaryVertices.Clear();
_temporaryUv.Clear();
_temporaryColors.Clear();
var tmpV = new System.ArraySegment<Vector3>(_vertices, 0, _vertexCount);
var tmpUv = new System.ArraySegment<Vector2>(_uv, 0, _vertexCount);
var tmpC = new System.ArraySegment<Color32>(_colors, 0, _vertexCount);
_temporaryVertices.AddRange(tmpV);
_temporaryUv.AddRange(tmpUv);
_temporaryColors.AddRange(tmpC);
_mesh.SetVertices(_temporaryVertices);
_mesh.SetUVs(0, _temporaryUv);
_mesh.SetColors(_temporaryColors);
UnityEngine.Profiling.Profiler.EndSample();
}
else // 半分以上使っている場合、そのまま渡す。
{
UnityEngine.Profiling.Profiler.BeginSample("DebugPrimitiveRenderer.UpdateMesh.CopyAll");
_mesh.vertices = _vertices;
_mesh.uv = _uv;
_mesh.colors32 = _colors;
UnityEngine.Profiling.Profiler.EndSample();
}
_mesh.subMeshCount = _subMeshCount;
materials = new Material[_subMeshCount];
for (int i = 0; i < _subMeshCount; i++)
{
materials[i] = _subMeshes[i].material;
}
_meshRenderer.sharedMaterials = materials;
var matrix = Matrix4x4.identity;
for (int i = 0; i < _subMeshCount; i++)
{
UnityEngine.Profiling.Profiler.BeginSample("DebugPrimitiveRenderer.UpdateMesh.FillIndices");
var subMesh = _subMeshes[i];
_temporaryIndices.Clear();
var tmpI = new System.ArraySegment<int>(_indices, subMesh.indexStart, subMesh.indexCount);
_temporaryIndices.AddRange(tmpI);
_mesh.SetTriangles(_temporaryIndices, i, true);
_materialPropertyBlock.SetTexture(
_textureShaderPropertyId,
subMesh.texture);
_meshRenderer.SetPropertyBlock(_materialPropertyBlock, i);
UnityEngine.Profiling.Profiler.EndSample();
}
}
_meshFilter.sharedMesh = _mesh;
_vertexCount = 0;
_indexCount = 0;
_texture = null;
// 毎フレーム白にリセット
color = new Color32(255, 255, 255, 255);
_subMeshCount = 0;
// どうもおかしいので毎フレーム取ってみる。
CharacterInfo ch;
_font.GetCharacterInfo(_whiteChar, out ch);
_whiteUv = ch.uvTopLeft;
_whiteUv += ch.uvTopRight;
_whiteUv += ch.uvBottomLeft;
_whiteUv += ch.uvBottomRight;
_whiteUv *= 0.25f;
}
// ■の中心のUVを取り直す
void OnFontTextureRebuilt(Font font)
{
if (font == _font)
{
this.fontTexture = font.material.mainTexture; // テクスチャ別物になってる可能性があるので刺しなおし
CharacterInfo ch;
_font.GetCharacterInfo(_whiteChar, out ch);
_whiteUv = ch.uvTopLeft;
_whiteUv += ch.uvTopRight;
_whiteUv += ch.uvBottomLeft;
_whiteUv += ch.uvBottomRight;
_whiteUv *= 0.25f;
}
}
public void SetTexture(Texture texture)
{
if (_texture != texture)
{
// ここまででSubMeshを終わらせる
AddSubMesh(texture);
_texture = texture;
}
}
void AddSubMesh(Texture texture, int minimumIndexCount = 0)
{
// 現インデクス数を記録
if (_subMeshCount > 0)
{
_subMeshes[_subMeshCount - 1].FixIndexCount(_indexCount);
}
SubMesh subMesh = null;
// 足りていれば使う。ただしインデクスは作り直す。TODO: もっとマシにできる。何フレームか経ったら使い回す、ということはできるはず。
if (_subMeshCount < _subMeshes.Count)
{
subMesh = _subMeshes[_subMeshCount];
}
// 足りなければ足す
else
{
subMesh = new SubMesh();
subMesh.indexStart = _indexCount;
_subMeshes.Add(subMesh);
}
// フォントテクスチャならテキストシェーダが差さったマテリアルを選択
if (texture == _font.material.mainTexture)
{
subMesh.material = _textMaterial;
}
else
{
subMesh.material = _texturedMaterial;
}
subMesh.texture = texture;
_subMeshCount++;
_indexCount = 0;
}
// 時計回りの相対頂点番号を3つ設定して三角形を生成
protected void AddTriangleIndices(int i0, int i1, int i2)
{
_indices[_indexCount + 0] = _vertexCount + i0;
_indices[_indexCount + 1] = _vertexCount + i1;
_indices[_indexCount + 2] = _vertexCount + i2;
_indexCount += 3;
}
// 時計回り4頂点で三角形を2個生成
protected void AddQuadIndices(int i0, int i1, int i2, int i3)
{
_indices[_indexCount + 0] = _vertexCount + i0;
_indices[_indexCount + 1] = _vertexCount + i1;
_indices[_indexCount + 2] = _vertexCount + i2;
_indices[_indexCount + 3] = _vertexCount + i2;
_indices[_indexCount + 4] = _vertexCount + i3;
_indices[_indexCount + 5] = _vertexCount + i0;
_indexCount += 6;
}
protected void AddIndices(IList<ushort> src)
{
var count = src.Count;
for (int i = 0; i < count; i++)
{
_indices[_indexCount + i] = _vertexCount + src[i];
}
_indexCount += count;
}
protected void AddIndices(IList<int> src)
{
var count = src.Count;
for (int i = 0; i < count; i++)
{
_indices[_indexCount + i] = _vertexCount + src[i];
}
_indexCount += count;
}
// 書き込み行数を返す
protected int AddTextNormalized(
out float widthOut,
out float heightOut,
string text,
float boxWidth,
float boxHeight,
float lineSpacingRatio,
bool wrap)
{
UnityEngine.Profiling.Profiler.BeginSample("DebugPrimitiveRenderer.AddTextNormalized");
int letterCount = text.Length;
_font.RequestCharactersInTexture(text);
SetTexture(fontTexture);
var verticesBegin = _vertexCount;
widthOut = heightOut = 0f;
// 高さが不足して一行も入らないケースに対処
var lineHeight = (float)_font.lineHeight;
if (lineHeight > boxHeight)
{
return 0;
}
heightOut = lineHeight;
// 二行目以降行間込みにする
lineHeight *= (1f + lineSpacingRatio);
// まず原点開始、z=0,xyがfont内整数座標、という状態で頂点を生成してしまう。
var pos = 0;
var lines = 1;
var p = Vector2.zero;
p.y += _font.ascent;
var waitNewLine = false;
while (pos < letterCount)
{
CharacterInfo ch;
var c = text[pos];
if (c == '\n')
{
waitNewLine = false;
p.y += lineHeight;
p.x = 0f;
// 縦はみ出しは即時終了
if ((heightOut + lineHeight) > boxHeight)
{
break;
}
heightOut += lineHeight;
lines++;
}
else if (!waitNewLine && _font.GetCharacterInfo(c, out ch))
{
if ((p.x + ch.advance) > boxWidth) // 横にはみ出した
{
if (wrap) // 折り返し
{
p.y += lineHeight;
p.x = 0f;
// 縦はみ出しは即時終了
if ((heightOut + lineHeight) > boxHeight)
{
break;
}
heightOut += lineHeight;
lines++;
}
else // 次の改行まで捨てる
{
waitNewLine = true;
break;
}
}
if (!AddCharNormalized(ref p, ref ch))
{
break;
}
p.x += ch.advance;
widthOut = Mathf.Max(p.x, widthOut);
}
pos++;
}
UnityEngine.Profiling.Profiler.EndSample();
return lines;
}
bool AddCharNormalized(
ref Vector2 p, // 原点(Xは左端、Yは上端+Font.ascent)
ref CharacterInfo ch)
{
if (((_vertexCount + 4) > _capacity) || ((_indexCount + 6) > _capacity))
{
return false;
}
float x = (float)(ch.minX);
float y = (float)(-ch.maxY);
float w = (float)(ch.maxX - ch.minX);
float h = (float)(ch.maxY - ch.minY);
var p0 = new Vector3(p.x + x, p.y + y, 0f); // 左上
var p1 = new Vector3(p0.x + w, p0.y, 0f); // 右上
var p2 = new Vector3(p1.x, p0.y + h, 0f); // 右下
var p3 = new Vector3(p0.x, p2.y, 0f); // 左下
// 頂点は左上から時計回り
_vertices[_vertexCount + 0] = p0;
_vertices[_vertexCount + 1] = p1;
_vertices[_vertexCount + 2] = p2;
_vertices[_vertexCount + 3] = p3;
_uv[_vertexCount + 0] = ch.uvTopLeft;
_uv[_vertexCount + 1] = ch.uvTopRight;
_uv[_vertexCount + 2] = ch.uvBottomRight;
_uv[_vertexCount + 3] = ch.uvBottomLeft;
for (int j = 0; j < 4; j++)
{
_colors[_vertexCount + j] = color;
}
AddQuadIndices(0, 1, 2, 3);
_vertexCount += 4;
return true;
}
protected void TransformVertices(int verticesBegin, ref Matrix4x4 matrix)
{
int vertexCount = _vertexCount - verticesBegin;
for (int i = 0; i < vertexCount; i++)
{
var v = _vertices[verticesBegin + i];
v = matrix.MultiplyPoint(v);
_vertices[verticesBegin + i] = v;
}
}
protected void TransformVertices(int verticesBegin, float scale, ref Vector2 translation)
{
int vertexCount = _vertexCount - verticesBegin;
for (int i = 0; i < vertexCount; i++)
{
var v = _vertices[verticesBegin + i];
v.x *= scale;
v.y *= scale;
v.x += translation.x;
v.y += translation.y;
_vertices[verticesBegin + i] = v;
}
}
// 以下未実装。箱だけ作っておく。数字の文字列化に伴うGC Alloc抹殺のため
public static void ToStringAsCharArray(out int writeCount, char[] dst, int dstPos, int value, int precision)
{
writeCount = 0;
}
public static void ToStringAsCharArray(out int writeCount, char[] dst, int dstPos, float value, int precision)
{
writeCount = 0;
}
}
}