Tanoda
LeapTextRenderer.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 System;
10using System.Collections.Generic;
11using UnityEngine;
12#if UNITY_EDITOR
13using UnityEditor;
14#endif
15using Leap.Unity.Space;
16using Leap.Unity.Query;
18
20
21 [LeapGraphicTag("Text")]
22 [Serializable]
23 public class LeapTextRenderer : LeapRenderingMethod<LeapTextGraphic>, ISupportsAddRemove {
24 public const string DEFAULT_FONT = "Arial.ttf";
25 public const string DEFAULT_SHADER = "LeapMotion/GraphicRenderer/Text/Dynamic";
26 public const float SCALE_CONSTANT = 0.001f;
27
28 [EditTimeOnly, SerializeField]
29 private Font _font = default(Font);
30
31 [EditTimeOnly, SerializeField]
32 private float _dynamicPixelsPerUnit = 1.0f;
33
34 [EditTimeOnly, SerializeField]
35 public bool _useColor = true;
36
37 [EditTimeOnly, SerializeField]
38 public Color _globalTint = Color.white;
39
40 [Header("Rendering Settings")]
41 [EditTimeOnly, SerializeField]
42 private Shader _shader = default(Shader);
43
44 [EditTimeOnly, SerializeField]
45 private float _scale = 1f;
46
47 #pragma warning disable 0649
48 [SerializeField]
49 private RendererMeshData _meshData;
50
51 [SerializeField]
52 private Material _material;
53 #pragma warning restore 0649
54
55 //Curved space
56 private const string CURVED_PARAMETERS = LeapGraphicRenderer.PROPERTY_PREFIX + "Curved_GraphicParameters";
57 private List<Matrix4x4> _curved_worldToAnchor = new List<Matrix4x4>();
58 private List<Vector4> _curved_graphicParameters = new List<Vector4>();
59
60 public override SupportInfo GetSpaceSupportInfo(LeapSpace space) {
61 return SupportInfo.FullSupport();
62 }
63
64 public void OnAddRemoveGraphics(List<int> dirtyIndexes) {
65 while (_meshData.Count > group.graphics.Count) {
66 _meshData.RemoveMesh(_meshData.Count - 1);
67 }
68
69 while (_meshData.Count < group.graphics.Count) {
70 group.graphics[_meshData.Count].isRepresentationDirty = true;
71 _meshData.AddMesh(new Mesh());
72 }
73 }
74
75 public override void OnEnableRenderer() {
76 foreach (var graphic in group.graphics) {
77 var textGraphic = graphic as LeapTextGraphic;
78 _font.RequestCharactersInTexture(textGraphic.text);
79 }
80
81 generateMaterial();
82
83 Font.textureRebuilt += onFontTextureRebuild;
84 }
85
86 public override void OnDisableRenderer() {
87 Font.textureRebuilt -= onFontTextureRebuild;
88 }
89
90 public override void OnUpdateRenderer() {
91 using (new ProfilerSample("Update Text Renderer")) {
92 ensureFontIsUpToDate();
93
94 for (int i = 0; i < group.graphics.Count; i++) {
95 var graphic = group.graphics[i] as LeapTextGraphic;
96
97 if (graphic.isRepresentationDirtyOrEditTime || graphic.HasRectChanged()) {
98 generateTextMesh(i, graphic, _meshData[i]);
99 }
100 }
101
102 if (renderer.space == null) {
103 using (new ProfilerSample("Draw Meshes")) {
104 for (int i = 0; i < group.graphics.Count; i++) {
105 var graphic = group.graphics[i];
106 if (graphic.isActiveAndEnabled) {
107 Graphics.DrawMesh(_meshData[i], graphic.transform.localToWorldMatrix, _material, 0);
108 }
109 }
110 }
111 } else if (renderer.space is LeapRadialSpace) {
112 var curvedSpace = renderer.space as LeapRadialSpace;
113
114 using (new ProfilerSample("Build Material Data And Draw Meshes")) {
115 _curved_worldToAnchor.Clear();
116 _curved_graphicParameters.Clear();
117 for (int i = 0; i < _meshData.Count; i++) {
118 var graphic = group.graphics[i];
119 if (!graphic.isActiveAndEnabled) {
120 _curved_graphicParameters.Add(Vector4.zero);
121 _curved_worldToAnchor.Add(Matrix4x4.identity);
122 continue;
123 }
124
125 var transformer = graphic.anchor.transformer;
126
127 Vector3 localPos = renderer.transform.InverseTransformPoint(graphic.transform.position);
128
129 Matrix4x4 mainTransform = renderer.transform.localToWorldMatrix * transformer.GetTransformationMatrix(localPos);
130 Matrix4x4 deform = renderer.transform.worldToLocalMatrix * Matrix4x4.TRS(renderer.transform.position - graphic.transform.position, Quaternion.identity, Vector3.one) * graphic.transform.localToWorldMatrix;
131 Matrix4x4 total = mainTransform * deform;
132
133 _curved_graphicParameters.Add((transformer as IRadialTransformer).GetVectorRepresentation(graphic.transform));
134 _curved_worldToAnchor.Add(mainTransform.inverse);
135
136 Graphics.DrawMesh(_meshData[i], total, _material, 0);
137 }
138 }
139
140 using (new ProfilerSample("Upload Material Data")) {
141 _material.SetFloat(SpaceProperties.RADIAL_SPACE_RADIUS, curvedSpace.radius);
142 _material.SetMatrixArraySafe("_GraphicRendererCurved_WorldToAnchor", _curved_worldToAnchor);
143 _material.SetMatrix("_GraphicRenderer_LocalToWorld", renderer.transform.localToWorldMatrix);
144 _material.SetVectorArraySafe("_GraphicRendererCurved_GraphicParameters", _curved_graphicParameters);
145 }
146 }
147 }
148 }
149
150#if UNITY_EDITOR
151 public override void OnEnableRendererEditor() {
152 base.OnEnableRendererEditor();
153
154 _font = Resources.GetBuiltinResource<Font>(DEFAULT_FONT);
155 _shader = Shader.Find(DEFAULT_SHADER);
156 }
157
158 public override void OnUpdateRendererEditor() {
159 base.OnUpdateRendererEditor();
160
161 if (_font == null) {
162 _font = Resources.GetBuiltinResource<Font>(DEFAULT_FONT);
163 }
164
165 if (_shader == null) {
166 _shader = Shader.Find(DEFAULT_SHADER);
167 }
168
169 _meshData.Validate(this);
170
171 //Make sure we have enough meshes to render all our graphics
172 while (_meshData.Count > group.graphics.Count) {
173 UnityEngine.Object.DestroyImmediate(_meshData[_meshData.Count - 1]);
174 _meshData.RemoveMesh(_meshData.Count - 1);
175 }
176
177 while (_meshData.Count < group.graphics.Count) {
178 _meshData.AddMesh(new Mesh());
179 }
180
181 generateMaterial();
182
183 PreventDuplication(ref _material);
184 }
185#endif
186
187 private void onFontTextureRebuild(Font font) {
188 if (font != _font) {
189 return;
190 }
191
192 foreach (var graphic in group.graphics) {
193 graphic.isRepresentationDirty = true;
194 }
195 }
196
197 private void generateMaterial() {
198 if (_material == null) {
199 _material = new Material(_font.material);
200 }
201
202#if UNITY_EDITOR
203 Undo.RecordObject(_material, "Touched material");
204#endif
205
206 _material.mainTexture = _font.material.mainTexture;
207 _material.name = "Font material";
208 _material.shader = _shader;
209
210 foreach (var keyword in _material.shaderKeywords) {
211 _material.DisableKeyword(keyword);
212 }
213
214 if (renderer.space != null) {
216 _material.EnableKeyword(SpaceProperties.CYLINDRICAL_FEATURE);
217 } else if (renderer.space is LeapSphericalSpace) {
218 _material.EnableKeyword(SpaceProperties.SPHERICAL_FEATURE);
219 }
220 }
221
222 if (_useColor) {
223 _material.EnableKeyword(LeapGraphicRenderer.FEATURE_PREFIX + "VERTEX_COLORS");
224 }
225 }
226
227 private void ensureFontIsUpToDate() {
228 CharacterInfo info;
229 bool doesNeedRebuild = false;
230
231 for (int i = 0; i < group.graphics.Count; i++) {
232 var graphic = group.graphics[i] as LeapTextGraphic;
233 int scaledFontSize = Mathf.RoundToInt(graphic.fontSize * _dynamicPixelsPerUnit);
234
235 if (graphic.isRepresentationDirtyOrEditTime) {
236 for (int j = 0; j < graphic.text.Length; j++) {
237 char character = graphic.text[j];
238
239 if (!_font.GetCharacterInfo(character, out info, scaledFontSize, graphic.fontStyle)) {
240 doesNeedRebuild = true;
241 break;
242 }
243 }
244
245 if (doesNeedRebuild) {
246 break;
247 }
248 }
249 }
250
251 if (!doesNeedRebuild) {
252 return;
253 }
254
255 for (int i = 0; i < group.graphics.Count; i++) {
256 var graphic = group.graphics[i] as LeapTextGraphic;
257 int scaledFontSize = Mathf.RoundToInt(graphic.fontSize * _dynamicPixelsPerUnit);
258
259 graphic.isRepresentationDirty = true;
260 _font.RequestCharactersInTexture(graphic.text,
261 scaledFontSize,
262 graphic.fontStyle);
263 }
264 }
265
266 private List<TextWrapper.Line> _tempLines = new List<TextWrapper.Line>();
267 private List<Vector3> _verts = new List<Vector3>();
268 private List<Vector4> _uvs = new List<Vector4>();
269 private List<Color> _colors = new List<Color>();
270 private List<int> _tris = new List<int>();
271 private void generateTextMesh(int index, LeapTextGraphic graphic, Mesh mesh) {
272 using (new ProfilerSample("Generate Text Mesh")) {
273 mesh.Clear(keepVertexLayout: false);
274
275 graphic.isRepresentationDirty = false;
276
277 int scaledFontSize = Mathf.RoundToInt(graphic.fontSize * _dynamicPixelsPerUnit);
278
279 //Check for characters not found in the font
280 {
281 HashSet<char> unfoundCharacters = null;
282 CharacterInfo info;
283 foreach (var character in graphic.text) {
284 if (character == '\n') {
285 continue;
286 }
287
288 if (unfoundCharacters != null && unfoundCharacters.Contains(character)) {
289 continue;
290 }
291
292 if (!_font.GetCharacterInfo(character, out info, scaledFontSize, graphic.fontStyle)) {
293 if (unfoundCharacters == null) unfoundCharacters = new HashSet<char>();
294 unfoundCharacters.Add(character);
295 Debug.LogError("Could not find character [" + character + "] in font " + _font + "!");
296 }
297 }
298 }
299
300 var text = graphic.text;
301
302 float _charScale = this._scale * SCALE_CONSTANT / _dynamicPixelsPerUnit;
303 float _scale = _charScale * graphic.fontSize / _font.fontSize;
304 float lineHeight = _scale * graphic.lineSpacing * _font.lineHeight * _dynamicPixelsPerUnit;
305
306 RectTransform rectTransform = graphic.transform as RectTransform;
307 float maxWidth;
308 if (rectTransform != null) {
309 maxWidth = rectTransform.rect.width;
310 } else {
311 maxWidth = float.MaxValue;
312 }
313
314 _widthCalculator.font = _font;
315 _widthCalculator.charScale = _charScale;
316 _widthCalculator.fontStyle = graphic.fontStyle;
317 _widthCalculator.scaledFontSize = scaledFontSize;
318 TextWrapper.Wrap(text, graphic.tokens, _tempLines, _widthCalculator.func, maxWidth);
319
320 float textHeight = _tempLines.Count * lineHeight;
321
322 Vector3 origin = Vector3.zero;
323 origin.y -= _font.ascent * _scale * _dynamicPixelsPerUnit;
324
325 if (rectTransform != null) {
326 origin.y -= rectTransform.rect.y;
327
328 switch (graphic.verticalAlignment) {
329 case LeapTextGraphic.VerticalAlignment.Center:
330 origin.y -= (rectTransform.rect.height - textHeight) / 2;
331 break;
332 case LeapTextGraphic.VerticalAlignment.Bottom:
333 origin.y -= (rectTransform.rect.height - textHeight);
334 break;
335 }
336 }
337
338 foreach (var line in _tempLines) {
339
340 if (rectTransform != null) {
341 origin.x = rectTransform.rect.x;
342 switch (graphic.horizontalAlignment) {
343 case LeapTextGraphic.HorizontalAlignment.Center:
344 origin.x += (rectTransform.rect.width - line.width) / 2;
345 break;
346 case LeapTextGraphic.HorizontalAlignment.Right:
347 origin.x += (rectTransform.rect.width - line.width);
348 break;
349 }
350 } else {
351 switch (graphic.horizontalAlignment) {
352 case LeapTextGraphic.HorizontalAlignment.Left:
353 origin.x = 0;
354 break;
355 case LeapTextGraphic.HorizontalAlignment.Center:
356 origin.x = -line.width / 2;
357 break;
358 case LeapTextGraphic.HorizontalAlignment.Right:
359 origin.x = -line.width;
360 break;
361 }
362 }
363
364 for (int i = line.start; i < line.end; i++) {
365 char c = text[i];
366
367 CharacterInfo info;
368 if (!_font.GetCharacterInfo(c, out info, scaledFontSize, graphic.fontStyle)) {
369 continue;
370 }
371
372 int offset = _verts.Count;
373 _tris.Add(offset + 0);
374 _tris.Add(offset + 1);
375 _tris.Add(offset + 2);
376
377 _tris.Add(offset + 0);
378 _tris.Add(offset + 2);
379 _tris.Add(offset + 3);
380
381 _verts.Add(_charScale * new Vector3(info.minX, info.maxY, 0) + origin);
382 _verts.Add(_charScale * new Vector3(info.maxX, info.maxY, 0) + origin);
383 _verts.Add(_charScale * new Vector3(info.maxX, info.minY, 0) + origin);
384 _verts.Add(_charScale * new Vector3(info.minX, info.minY, 0) + origin);
385
386 _uvs.Add(info.uvTopLeft);
387 _uvs.Add(info.uvTopRight);
388 _uvs.Add(info.uvBottomRight);
389 _uvs.Add(info.uvBottomLeft);
390
391 if (_useColor) {
392 _colors.Append(4, _globalTint * graphic.color);
393 }
394
395 origin.x += info.advance * _charScale;
396 }
397 origin.y -= lineHeight;
398 }
399
400 for (int i = 0; i < _uvs.Count; i++) {
401 Vector4 uv = _uvs[i];
402 uv.w = index;
403 _uvs[i] = uv;
404 }
405
406 mesh.SetVertices(_verts);
407 mesh.SetTriangles(_tris, 0);
408 mesh.SetUVs(0, _uvs);
409
410 if (_useColor) {
411 mesh.SetColors(_colors);
412 }
413
414 _verts.Clear();
415 _uvs.Clear();
416 _tris.Clear();
417 _colors.Clear();
418 _tempLines.Clear();
419 }
420 }
421
422 private CharWidthCalculator _widthCalculator = new CharWidthCalculator();
423 private class CharWidthCalculator {
424 public Font font;
425 public int scaledFontSize;
426 public FontStyle fontStyle;
427 public float charScale;
428
429 public Func<char, float> func;
430
431 public CharWidthCalculator() {
432 func = funcMethod;
433 }
434
435 private float funcMethod(char c) {
436 CharacterInfo info;
437 font.GetCharacterInfo(c, out info, scaledFontSize, fontStyle);
438 return info.advance * charScale;
439 }
440 }
441 }
442}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
List< LeapGraphic > graphics
Returns the list of graphics attached to this group. This getter returns a regular mutable list for s...
LeapSpace space
Returns the leap space that is currently attached to this graphic renderer.
LeapGraphicGroup group
Gets the group this rendering method is attached to.
LeapGraphicRenderer renderer
Gets the renderer this rendering method is attached to.
void OnAddRemoveGraphics(List< int > dirtyIndexes)
Must be implemented by a renderer to report that it is able to support adding and removing graphics a...
override SupportInfo GetSpaceSupportInfo(LeapSpace space)
override void OnEnableRenderer()
Called when the renderer is enabled at runtime.
override void OnUpdateRenderer()
Called from LateUpdate during runtime. Use this to update the renderer using any changes made to duri...
override void OnDisableRenderer()
Called when the renderer is disabled at runtime.
void Validate(LeapRenderingMethod renderingMethod)
The support info class provides a very basic way to notify that something is fully supported,...
Definition: SupportInfo.cs:21
static SupportInfo FullSupport()
Helper getter to return a struct that signifies full support.
Definition: SupportInfo.cs:28
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....