Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
743f6b6f9This file is pinned to the selected release. Download the original to open it in your design tool.
/******************************************************************************
* Copyright (C) Leap Motion, Inc. 2011-2018. *
* Leap Motion proprietary and confidential. *
* *
* Use subject to the terms of the Leap Motion SDK Agreement available at *
* https://developer.leapmotion.com/sdk_agreement, or another agreement *
* between Leap Motion and you, your company or other organization. *
******************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using Leap.Unity.Space;
using Leap.Unity.Query;
using Leap.Unity.Attributes;
namespace Leap.Unity.GraphicalRenderer {
[LeapGraphicTag("Text")]
[Serializable]
public class LeapTextRenderer : LeapRenderingMethod<LeapTextGraphic>, ISupportsAddRemove {
public const string DEFAULT_FONT = "Arial.ttf";
public const string DEFAULT_SHADER = "LeapMotion/GraphicRenderer/Text/Dynamic";
public const float SCALE_CONSTANT = 0.001f;
[EditTimeOnly, SerializeField]
private Font _font;
[EditTimeOnly, SerializeField]
private float _dynamicPixelsPerUnit = 1.0f;
[EditTimeOnly, SerializeField]
public bool _useColor = true;
[EditTimeOnly, SerializeField]
public Color _globalTint = Color.white;
[Header("Rendering Settings")]
[EditTimeOnly, SerializeField]
private Shader _shader;
[EditTimeOnly, SerializeField]
private float _scale = 1f;
[SerializeField]
private RendererMeshData _meshData;
[SerializeField]
private Material _material;
//Curved space
private const string CURVED_PARAMETERS = LeapGraphicRenderer.PROPERTY_PREFIX + "Curved_GraphicParameters";
private List<Matrix4x4> _curved_worldToAnchor = new List<Matrix4x4>();
private List<Vector4> _curved_graphicParameters = new List<Vector4>();
public override SupportInfo GetSpaceSupportInfo(LeapSpace space) {
return SupportInfo.FullSupport();
}
public void OnAddRemoveGraphics(List<int> dirtyIndexes) {
while (_meshData.Count > group.graphics.Count) {
_meshData.RemoveMesh(_meshData.Count - 1);
}
while (_meshData.Count < group.graphics.Count) {
group.graphics[_meshData.Count].isRepresentationDirty = true;
_meshData.AddMesh(new Mesh());
}
}
public override void OnEnableRenderer() {
foreach (var graphic in group.graphics) {
var textGraphic = graphic as LeapTextGraphic;
_font.RequestCharactersInTexture(textGraphic.text);
}
generateMaterial();
Font.textureRebuilt += onFontTextureRebuild;
}
public override void OnDisableRenderer() {
Font.textureRebuilt -= onFontTextureRebuild;
}
public override void OnUpdateRenderer() {
using (new ProfilerSample("Update Text Renderer")) {
ensureFontIsUpToDate();
for (int i = 0; i < group.graphics.Count; i++) {
var graphic = group.graphics[i] as LeapTextGraphic;
if (graphic.isRepresentationDirtyOrEditTime || graphic.HasRectChanged()) {
generateTextMesh(i, graphic, _meshData[i]);
}
}
if (renderer.space == null) {
using (new ProfilerSample("Draw Meshes")) {
for (int i = 0; i < group.graphics.Count; i++) {
var graphic = group.graphics[i];
if (graphic.isActiveAndEnabled) {
Graphics.DrawMesh(_meshData[i], graphic.transform.localToWorldMatrix, _material, 0);
}
}
}
} else if (renderer.space is LeapRadialSpace) {
var curvedSpace = renderer.space as LeapRadialSpace;
using (new ProfilerSample("Build Material Data And Draw Meshes")) {
_curved_worldToAnchor.Clear();
_curved_graphicParameters.Clear();
for (int i = 0; i < _meshData.Count; i++) {
var graphic = group.graphics[i];
if (!graphic.isActiveAndEnabled) {
_curved_graphicParameters.Add(Vector4.zero);
_curved_worldToAnchor.Add(Matrix4x4.identity);
continue;
}
var transformer = graphic.anchor.transformer;
Vector3 localPos = renderer.transform.InverseTransformPoint(graphic.transform.position);
Matrix4x4 mainTransform = renderer.transform.localToWorldMatrix * transformer.GetTransformationMatrix(localPos);
Matrix4x4 deform = renderer.transform.worldToLocalMatrix * Matrix4x4.TRS(renderer.transform.position - graphic.transform.position, Quaternion.identity, Vector3.one) * graphic.transform.localToWorldMatrix;
Matrix4x4 total = mainTransform * deform;
_curved_graphicParameters.Add((transformer as IRadialTransformer).GetVectorRepresentation(graphic.transform));
_curved_worldToAnchor.Add(mainTransform.inverse);
Graphics.DrawMesh(_meshData[i], total, _material, 0);
}
}
using (new ProfilerSample("Upload Material Data")) {
_material.SetFloat(SpaceProperties.RADIAL_SPACE_RADIUS, curvedSpace.radius);
_material.SetMatrixArraySafe("_GraphicRendererCurved_WorldToAnchor", _curved_worldToAnchor);
_material.SetMatrix("_GraphicRenderer_LocalToWorld", renderer.transform.localToWorldMatrix);
_material.SetVectorArraySafe("_GraphicRendererCurved_GraphicParameters", _curved_graphicParameters);
}
}
}
}
#if UNITY_EDITOR
public override void OnEnableRendererEditor() {
base.OnEnableRendererEditor();
_font = Resources.GetBuiltinResource<Font>(DEFAULT_FONT);
_shader = Shader.Find(DEFAULT_SHADER);
}
public override void OnUpdateRendererEditor() {
base.OnUpdateRendererEditor();
if (_font == null) {
_font = Resources.GetBuiltinResource<Font>(DEFAULT_FONT);
}
if (_shader == null) {
_shader = Shader.Find(DEFAULT_SHADER);
}
_meshData.Validate(this);
//Make sure we have enough meshes to render all our graphics
while (_meshData.Count > group.graphics.Count) {
UnityEngine.Object.DestroyImmediate(_meshData[_meshData.Count - 1]);
_meshData.RemoveMesh(_meshData.Count - 1);
}
while (_meshData.Count < group.graphics.Count) {
_meshData.AddMesh(new Mesh());
}
generateMaterial();
PreventDuplication(ref _material);
}
#endif
private void onFontTextureRebuild(Font font) {
if (font != _font) {
return;
}
foreach (var graphic in group.graphics) {
graphic.isRepresentationDirty = true;
}
}
private void generateMaterial() {
if (_material == null) {
_material = new Material(_font.material);
}
#if UNITY_EDITOR
Undo.RecordObject(_material, "Touched material");
#endif
_material.mainTexture = _font.material.mainTexture;
_material.name = "Font material";
_material.shader = _shader;
foreach (var keyword in _material.shaderKeywords) {
_material.DisableKeyword(keyword);
}
if (renderer.space != null) {
if (renderer.space is LeapCylindricalSpace) {
_material.EnableKeyword(SpaceProperties.CYLINDRICAL_FEATURE);
} else if (renderer.space is LeapSphericalSpace) {
_material.EnableKeyword(SpaceProperties.SPHERICAL_FEATURE);
}
}
if (_useColor) {
_material.EnableKeyword(LeapGraphicRenderer.FEATURE_PREFIX + "VERTEX_COLORS");
}
}
private void ensureFontIsUpToDate() {
CharacterInfo info;
bool doesNeedRebuild = false;
for (int i = 0; i < group.graphics.Count; i++) {
var graphic = group.graphics[i] as LeapTextGraphic;
int scaledFontSize = Mathf.RoundToInt(graphic.fontSize * _dynamicPixelsPerUnit);
if (graphic.isRepresentationDirtyOrEditTime) {
for (int j = 0; j < graphic.text.Length; j++) {
char character = graphic.text[j];
if (!_font.GetCharacterInfo(character, out info, scaledFontSize, graphic.fontStyle)) {
doesNeedRebuild = true;
break;
}
}
if (doesNeedRebuild) {
break;
}
}
}
if (!doesNeedRebuild) {
return;
}
for (int i = 0; i < group.graphics.Count; i++) {
var graphic = group.graphics[i] as LeapTextGraphic;
int scaledFontSize = Mathf.RoundToInt(graphic.fontSize * _dynamicPixelsPerUnit);
graphic.isRepresentationDirty = true;
_font.RequestCharactersInTexture(graphic.text,
scaledFontSize,
graphic.fontStyle);
}
}
private List<TextWrapper.Line> _tempLines = new List<TextWrapper.Line>();
private List<Vector3> _verts = new List<Vector3>();
private List<Vector4> _uvs = new List<Vector4>();
private List<Color> _colors = new List<Color>();
private List<int> _tris = new List<int>();
private void generateTextMesh(int index, LeapTextGraphic graphic, Mesh mesh) {
using (new ProfilerSample("Generate Text Mesh")) {
mesh.Clear(keepVertexLayout: false);
graphic.isRepresentationDirty = false;
int scaledFontSize = Mathf.RoundToInt(graphic.fontSize * _dynamicPixelsPerUnit);
//Check for characters not found in the font
{
HashSet<char> unfoundCharacters = null;
CharacterInfo info;
foreach (var character in graphic.text) {
if (character == '\n') {
continue;
}
if (unfoundCharacters != null && unfoundCharacters.Contains(character)) {
continue;
}
if (!_font.GetCharacterInfo(character, out info, scaledFontSize, graphic.fontStyle)) {
if (unfoundCharacters == null) unfoundCharacters = new HashSet<char>();
unfoundCharacters.Add(character);
Debug.LogError("Could not find character [" + character + "] in font " + _font + "!");
}
}
}
var text = graphic.text;
float _charScale = this._scale * SCALE_CONSTANT / _dynamicPixelsPerUnit;
float _scale = _charScale * graphic.fontSize / _font.fontSize;
float lineHeight = _scale * graphic.lineSpacing * _font.lineHeight * _dynamicPixelsPerUnit;
RectTransform rectTransform = graphic.transform as RectTransform;
float maxWidth;
if (rectTransform != null) {
maxWidth = rectTransform.rect.width;
} else {
maxWidth = float.MaxValue;
}
_widthCalculator.font = _font;
_widthCalculator.charScale = _charScale;
_widthCalculator.fontStyle = graphic.fontStyle;
_widthCalculator.scaledFontSize = scaledFontSize;
TextWrapper.Wrap(text, graphic.tokens, _tempLines, _widthCalculator.func, maxWidth);
float textHeight = _tempLines.Count * lineHeight;
Vector3 origin = Vector3.zero;
origin.y -= _font.ascent * _scale * _dynamicPixelsPerUnit;
if (rectTransform != null) {
origin.y -= rectTransform.rect.y;
switch (graphic.verticalAlignment) {
case LeapTextGraphic.VerticalAlignment.Center:
origin.y -= (rectTransform.rect.height - textHeight) / 2;
break;
case LeapTextGraphic.VerticalAlignment.Bottom:
origin.y -= (rectTransform.rect.height - textHeight);
break;
}
}
foreach (var line in _tempLines) {
if (rectTransform != null) {
origin.x = rectTransform.rect.x;
switch (graphic.horizontalAlignment) {
case LeapTextGraphic.HorizontalAlignment.Center:
origin.x += (rectTransform.rect.width - line.width) / 2;
break;
case LeapTextGraphic.HorizontalAlignment.Right:
origin.x += (rectTransform.rect.width - line.width);
break;
}
} else {
switch (graphic.horizontalAlignment) {
case LeapTextGraphic.HorizontalAlignment.Left:
origin.x = 0;
break;
case LeapTextGraphic.HorizontalAlignment.Center:
origin.x = -line.width / 2;
break;
case LeapTextGraphic.HorizontalAlignment.Right:
origin.x = -line.width;
break;
}
}
for (int i = line.start; i < line.end; i++) {
char c = text[i];
CharacterInfo info;
if (!_font.GetCharacterInfo(c, out info, scaledFontSize, graphic.fontStyle)) {
continue;
}
int offset = _verts.Count;
_tris.Add(offset + 0);
_tris.Add(offset + 1);
_tris.Add(offset + 2);
_tris.Add(offset + 0);
_tris.Add(offset + 2);
_tris.Add(offset + 3);
_verts.Add(_charScale * new Vector3(info.minX, info.maxY, 0) + origin);
_verts.Add(_charScale * new Vector3(info.maxX, info.maxY, 0) + origin);
_verts.Add(_charScale * new Vector3(info.maxX, info.minY, 0) + origin);
_verts.Add(_charScale * new Vector3(info.minX, info.minY, 0) + origin);
_uvs.Add(info.uvTopLeft);
_uvs.Add(info.uvTopRight);
_uvs.Add(info.uvBottomRight);
_uvs.Add(info.uvBottomLeft);
if (_useColor) {
_colors.Append(4, _globalTint * graphic.color);
}
origin.x += info.advance * _charScale;
}
origin.y -= lineHeight;
}
for (int i = 0; i < _uvs.Count; i++) {
Vector4 uv = _uvs[i];
uv.w = index;
_uvs[i] = uv;
}
mesh.SetVertices(_verts);
mesh.SetTriangles(_tris, 0);
mesh.SetUVs(0, _uvs);
if (_useColor) {
mesh.SetColors(_colors);
}
_verts.Clear();
_uvs.Clear();
_tris.Clear();
_colors.Clear();
_tempLines.Clear();
}
}
private CharWidthCalculator _widthCalculator = new CharWidthCalculator();
private class CharWidthCalculator {
public Font font;
public int scaledFontSize;
public FontStyle fontStyle;
public float charScale;
public Func<char, float> func;
public CharWidthCalculator() {
func = funcMethod;
}
private float funcMethod(char c) {
CharacterInfo info;
font.GetCharacterInfo(c, out info, scaledFontSize, fontStyle);
return info.advance * charScale;
}
}
}
}