Tilt Five™ Unity API  1.4.1
TableTopGizmo.cs
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020-2023 Tilt Five, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 using UnityEngine;
18 
19 #if UNITY_EDITOR
20 using UnityEditor;
21 using System.Collections.Generic;
22 
23 namespace TiltFive
24 {
28  public class TableTopGizmo {
29 
30  private string boardMeshAssetPath_LE = MeshAssets.GetPathToGameboardMesh(GameboardType.GameboardType_LE);
31  private string surfaceMeshChildPath_LE = "Surface_LE";
32  private string borderMeshChildPath_LE = "Border_LE";
33 
34  private string boardMeshAssetPath_XE = MeshAssets.GetPathToGameboardMesh(GameboardType.GameboardType_XE);
35  private string surfaceMeshChildPath_XE = "Surface_Flat";
36  private string borderMeshChildPath_XE = "Border_Flat";
37 
38  private string boardMeshAssetPath_XE_Raised = MeshAssets.GetPathToGameboardMesh(GameboardType.GameboardType_XE_Raised);
39  private string surfaceMeshChildPath_XE_Raised = "Surface_Raised";
40  private string borderMeshChildPath_XE_Raised = "Border_Raised";
41 
42  private string logoMeshAssetPath = MeshAssets.GetPathToT5LogoMesh();
43  private string logoBorderChildPath = "Circle";
44  private string logoLeftCharacterChildPath = "Tilt";
45  private string logoRightCharacterChildPath = "Five";
46 
47  private float gizmoAlpha;
48  private ScaleSettings scaleSettings;
49  private GameBoardSettings gameBoardSettings;
50  private float scaleToUWRLD_UGBD => scaleSettings.GetScaleToUWRLD_UGBD(gameBoardSettings.gameBoardScale);
51  private GameBoard.GameboardExtents gameboardExtents = new GameBoard.GameboardExtents(
52  GameBoard.GameboardExtents.GAMEBOARD_SIZE_LE);
53 
54  private float totalGameBoardWidthInMeters =>
55  usableGameBoardWidthInMeters + 2f * GameBoard.GameboardExtents.BORDER_WIDTH_IN_METERS;
56  private float totalGameBoardLengthInMeters =>
57  usableGameBoardLengthInMeters + 2f * GameBoard.GameboardExtents.BORDER_WIDTH_IN_METERS;
58 
59  private float usableGameBoardWidthInMeters => gameboardExtents.ViewableSpanX.ToMeters;
60  private float usableGameBoardLengthInMeters => gameboardExtents.ViewableSpanZ.ToMeters;
61 
62  private GameboardType gameboardType;
63 
64  private GameObject meshObj_LE;
65  private Mesh meshBorder_LE;
66  private Mesh meshSurface_LE;
67 
68  private GameObject meshObj_XE;
69  private Mesh meshBorder_XE;
70  private Mesh meshSurface_XE;
71 
72  private GameObject meshObj_XE_Raised;
73  private Mesh meshBorder_XE_Raised;
74  private Mesh meshSurface_XE_Raised;
75 
76  private GameObject logoObj;
77  private Mesh meshLogoBorder;
78  private Mesh meshLogoLeftCharacter;
79  private Mesh meshLogoRightCharacter;
80 
81  private Mesh meshRuler;
82  private List<LineSegment> rulerData;
83 
84 
85  // The lines comprising the board gizmo's unit grid.
86  private List<LineSegment> xGridLines;
87  private List<LineSegment> zGridLines;
88  private float yGridOffset;
89 
90  private struct LineSegment
91  {
92  public LineSegment(Vector3 start, Vector3 end, int LOD)
93  {
94  this.Start = start;
95  this.End = end;
96  this.LOD = LOD;
97  }
98  public Vector3 Start;
99  public Vector3 End;
100  public int LOD;
101  }
102 
103 
104  private void Configure(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings,
105  float alpha, float gridOffsetY, GameBoard.GameboardExtents gameboardExtents)
106  {
107  this.scaleSettings = scaleSettings;
108  this.gameBoardSettings = gameBoardSettings;
109  this.gizmoAlpha = alpha;
110  this.yGridOffset = gridOffsetY;
111  this.gameboardExtents = gameboardExtents;
112 
113  if (meshObj_LE == null || meshBorder_LE == null || meshSurface_LE == null)
114  {
115  LoadGameboardModel(boardMeshAssetPath_LE, surfaceMeshChildPath_LE, borderMeshChildPath_LE,
116  out meshObj_LE, out meshSurface_LE, out meshBorder_LE);
117  }
118  if (meshObj_XE == null || meshBorder_XE == null || meshSurface_XE == null)
119  {
120  LoadGameboardModel(boardMeshAssetPath_XE, surfaceMeshChildPath_XE, borderMeshChildPath_XE,
121  out meshObj_XE, out meshSurface_XE, out meshBorder_XE);
122  }
123  if (meshObj_XE_Raised == null || meshBorder_XE_Raised == null || meshSurface_XE_Raised == null)
124  {
125  LoadGameboardModel(boardMeshAssetPath_XE_Raised, surfaceMeshChildPath_XE_Raised, borderMeshChildPath_XE_Raised,
126  out meshObj_XE_Raised, out meshSurface_XE_Raised, out meshBorder_XE_Raised);
127  }
128 
129  if(null == logoObj)
130  {
131  logoObj = AssetDatabase.LoadAssetAtPath<GameObject>(logoMeshAssetPath);
132  }
133 
134  if(null == meshLogoBorder)
135  {
136  var transform = logoObj.transform.Find( logoBorderChildPath );
137  if(transform != null)
138  {
139  if(transform.TryGetComponent<MeshFilter>(out var meshFilter))
140  {
141  meshLogoBorder = meshFilter.sharedMesh;
142  }
143  }
144  }
145  if(null == meshLogoLeftCharacter)
146  {
147  var transform = logoObj.transform.Find( logoLeftCharacterChildPath );
148  if(transform != null)
149  {
150  if(transform.TryGetComponent<MeshFilter>(out var meshFilter))
151  {
152  meshLogoLeftCharacter = meshFilter.sharedMesh;
153  }
154  }
155  }
156  if(null == meshLogoRightCharacter)
157  {
158  var transform = logoObj.transform.Find( logoRightCharacterChildPath );
159  if(transform != null)
160  {
161  if(transform.TryGetComponent<MeshFilter>(out var meshFilter))
162  {
163  meshLogoRightCharacter = meshFilter.sharedMesh;
164  }
165  }
166  }
167 
168  var previousGameboardType = gameboardType;
169  gameboardType = gameBoardSettings.currentGameBoard.GetDisplayedGameboardType(gameBoardSettings);
170 
171  bool gameboardTypeChanged = previousGameboardType != gameboardType;
172 
173  if (null == xGridLines || null == zGridLines || gameboardTypeChanged)
174  {
175  ResetGrid();
176  }
177 
178  if(null == meshRuler || gameboardTypeChanged)
179  {
180  ConstructRulerMesh(gameboardExtents.ViewableSpanX, "meshRuler", out meshRuler);
181  }
182  }
183 
184  public void LoadGameboardModel(string meshAssetPath, string surfaceMeshChildPath, string borderMeshChildPath,
185  out GameObject meshObj, out Mesh meshSurface, out Mesh meshBorder)
186  {
187  meshObj = AssetDatabase.LoadAssetAtPath<GameObject>(meshAssetPath);
188 
189  var surfaceMeshTransform = meshObj.transform.Find(surfaceMeshChildPath);
190  if (surfaceMeshTransform != null)
191  {
192  meshSurface = surfaceMeshTransform.TryGetComponent<MeshFilter>(out var meshFilter) ? meshFilter.sharedMesh : null;
193  }
194  else
195  {
196  meshSurface = null;
197  }
198 
199  var borderMeshTransform = meshObj.transform.Find(borderMeshChildPath);
200  if (borderMeshTransform != null)
201  {
202  meshBorder = borderMeshTransform.TryGetComponent<MeshFilter>(out var meshFilter) ? meshFilter.sharedMesh : null;
203  }
204  else
205  {
206  meshBorder = null;
207  }
208  }
209 
210  public void ConstructRulerMesh(Length rulerLength, string meshName, out Mesh meshRuler)
211  {
212  var rulerData = new List<LineSegment>();
213 
214  float oneMillimeterLengthInMeters = new Length(1, LengthUnit.Millimeters).ToMeters;
215 
216  // For the centimeter ruler, we're going to draw regular marks for centimeters, and smaller ones for millimeters.
217  for (int i = 0; i * oneMillimeterLengthInMeters < rulerLength.ToMeters; i++)
218  {
219  float currentDistance = i * (oneMillimeterLengthInMeters / rulerLength.ToMeters);
220  float smallestFractionOfBoardWidth = 1f / 150f;
221 
222  float tickMarkLength = smallestFractionOfBoardWidth;
223  int lod = 3;
224 
225  lod -= i % 5 == 0 ? 1 : 0;
226  lod -= i % 10 == 0 ? 1 : 0;
227 
228  tickMarkLength += (3 - lod) * smallestFractionOfBoardWidth;
229 
230  rulerData.Add(new LineSegment(new Vector3(currentDistance, 0f, 0f), new Vector3(currentDistance, 0f, tickMarkLength), lod));
231  }
232 
233  float oneSixteenthInchLengthInMeters = new Length(1 / 16f, LengthUnit.Inches).ToMeters;
234 
235  // For the inch ruler, we're going to draw regular marks for inches, and smaller ones for half/quarter/eighth/sixteenth inches.
236  for (int i = 0; i * oneSixteenthInchLengthInMeters < rulerLength.ToMeters; i++)
237  {
238  float currentDistance = i * (oneSixteenthInchLengthInMeters / rulerLength.ToMeters);
239  float smallestFractionOfBoardWidth = 1f / 300f;
240 
241  float tickMarkLength = smallestFractionOfBoardWidth;
242  int lod = 5;
243 
244  lod -= i % 2 == 0 ? 1 : 0;
245  lod -= i % 4 == 0 ? 1 : 0;
246  lod -= i % 8 == 0 ? 1 : 0;
247  lod -= i % 16 == 0 ? 1 : 0;
248 
249  tickMarkLength += (5 - lod) * smallestFractionOfBoardWidth;
250 
251  float offsetFromCentimeterRuler = 1 / 16f;
252  rulerData.Add(new LineSegment(
253  new Vector3(currentDistance, 0f, offsetFromCentimeterRuler - tickMarkLength),
254  new Vector3(currentDistance, 0f, offsetFromCentimeterRuler),
255  lod));
256  }
257 
258  meshRuler = new Mesh();
259  meshRuler.name = meshName;
260 
261  int vertArraySizeRatio = 4; // There are 4 vertices for every line in rulerData.
262  Vector3[] verts = new Vector3[rulerData.Count * vertArraySizeRatio];
263 
264  int triArraySizeRatio = 6; // There are 6 triangle vertex indices for every line in rulerData.
265  int[] triangles = new int[rulerData.Count * triArraySizeRatio];
266 
267  float lineThickness = 1 / 2800f;
268 
269  // We want to offset the x vector component to achieve line thickness.
270  var lineThicknessOffset = Vector3.right * (lineThickness / 2f);
271 
272  for (int i = 0; i < rulerData.Count; i++)
273  {
274  var line = rulerData[i];
275 
276  var bottomLeft = line.Start - lineThicknessOffset + Vector3.left / 2 + Vector3.back / 32f;
277  var topLeft = line.End - lineThicknessOffset + Vector3.left / 2 + Vector3.back / 32f;
278 
279  var bottomRight = line.Start + lineThicknessOffset + Vector3.left / 2 + Vector3.back / 32f;
280  var topRight = line.End + lineThicknessOffset + Vector3.left / 2 + Vector3.back / 32f;
281 
282  var vertIndex = i * vertArraySizeRatio;
283  verts[vertIndex] = bottomLeft;
284  verts[vertIndex + 1] = topLeft;
285  verts[vertIndex + 2] = bottomRight;
286  verts[vertIndex + 3] = topRight;
287 
288  var triIndex = i * triArraySizeRatio;
289  triangles[triIndex] = vertIndex; // bottomLeft
290  triangles[triIndex + 1] = vertIndex + 1; // topLeft
291  triangles[triIndex + 2] = vertIndex + 2; // bottomRight
292 
293  triangles[triIndex + 3] = vertIndex + 2; // bottomRight
294  triangles[triIndex + 4] = vertIndex + 1; // topLeft
295  triangles[triIndex + 5] = vertIndex + 3; // topRight
296  }
297 
298  meshRuler.vertices = verts;
299  meshRuler.triangles = triangles;
300  meshRuler.RecalculateNormals();
301  }
302 
303  // Defines a series of line segments in gameboard space (-)
304  public void ResetGrid(ScaleSettings newScaleSettings = null, GameBoardSettings newGameBoardSettings = null)
305  {
306  if (newScaleSettings != null)
307  {
308  this.scaleSettings = newScaleSettings;
309  }
310  if(newGameBoardSettings != null)
311  {
312  this.gameBoardSettings = newGameBoardSettings;
313  }
314 
315  if(xGridLines == null)
316  {
317  xGridLines = new List<LineSegment>();
318  }
319  else xGridLines.Clear();
320 
321  if(zGridLines == null)
322  {
323  zGridLines = new List<LineSegment>();
324  }
325  else zGridLines.Clear();
326 
327  var lineLengthZ = 0.5f;
328  var linePosOffsetZ = lineLengthZ - 0.5f;
329  var lineStartPosZ = lineLengthZ;
330  var lineStopPosZ = -lineLengthZ + linePosOffsetZ;
331  var minDimension = Mathf.Min(usableGameBoardWidthInMeters, usableGameBoardLengthInMeters);
332  var oneUnit = scaleSettings.oneUnitLengthInMeters / minDimension;
333  // Starting from the center outward, define x-axis grid lines in 1-unit increments.
334  for (int i = 0; i * scaleSettings.oneUnitLengthInMeters < usableGameBoardWidthInMeters / 2; i++)
335  {
336  float distanceFromCenter = i * (scaleSettings.oneUnitLengthInMeters / minDimension);
337  int lod = 1; // TODO: Change this later
338 
339  xGridLines.Add(new LineSegment(new Vector3(distanceFromCenter, 0, lineStartPosZ), new Vector3(distanceFromCenter, 0, lineStopPosZ), lod));
340 
341  // No need to draw a second overlapping pair of lines along the origin when i == 0.
342  if(i < 1) {continue;}
343 
344  xGridLines.Add(new LineSegment(new Vector3(-distanceFromCenter, 0, lineStartPosZ), new Vector3(-distanceFromCenter, 0, lineStopPosZ), lod));
345  }
346 
347  // Starting from the center outward, define z-axis grid lines in 1-unit increments.
348  for (int i = 0; i * scaleSettings.oneUnitLengthInMeters < usableGameBoardWidthInMeters / 2; i++)
349  {
350  float distanceFromCenter = i * (scaleSettings.oneUnitLengthInMeters / minDimension);
351  int lod = 1; // TODO: Change this later
352 
353  zGridLines.Add(new LineSegment(new Vector3(0.5f, 0, distanceFromCenter), new Vector3(-0.5f, 0, distanceFromCenter), lod));
354 
355  // No need to draw a second overlapping pair of lines along the origin when i == 0.
356  if(i < scaleSettings.oneUnitLengthInMeters) { continue; }
357 
358  zGridLines.Add(new LineSegment(new Vector3(0.5f, 0, -distanceFromCenter), new Vector3(-0.5f, 0, -distanceFromCenter), lod));
359  }
360  }
361 
362 
363  public void Draw(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings,
364  float alpha, bool showGrid, float gridOffsetY = 0f)
365  {
366  Configure (scaleSettings, gameBoardSettings, alpha, gridOffsetY, gameboardExtents);
367 
368  if (null == gameBoardSettings) { return; }
369 
370 
371  Matrix4x4 mtxOrigin = Matrix4x4.TRS( Vector3.zero, Quaternion.identity, Vector3.one );
372 
373  Matrix4x4 mtxWorld = Matrix4x4.TRS( gameBoardSettings.gameBoardCenter,
374  Quaternion.Euler(gameBoardSettings.gameBoardRotation),
375  Vector3.one * scaleToUWRLD_UGBD);
376 
377  Matrix4x4 mtxPreTransform = Matrix4x4.TRS( Vector3.zero, Quaternion.Euler(0.0f, 0.0f, 0.0f), Vector3.one );
378 
379  Matrix4x4 mtxGizmo = mtxOrigin * mtxWorld * mtxPreTransform;
380 
381  Color oldColor = Gizmos.color;
382  Matrix4x4 oldMatrix = Gizmos.matrix;
383 
384  Gizmos.matrix = mtxGizmo;
385 
386  Mesh meshBorder, meshSurface;
387 
388  switch (gameboardType)
389  {
390  case GameboardType.GameboardType_XE:
391  meshBorder = meshBorder_XE;
392  meshSurface = meshSurface_XE;
393  break;
394  case GameboardType.GameboardType_XE_Raised:
395  meshBorder = meshBorder_XE_Raised;
396  meshSurface = meshSurface_XE_Raised;
397  break;
398  default:
399  case GameboardType.GameboardType_LE:
400  case GameboardType.GameboardType_None:
401  meshBorder = meshBorder_LE;
402  meshSurface = meshSurface_LE;
403  break;
404  }
405 
406  if(meshBorder != null)
407  {
408  Gizmos.color = new Color(0.0f, 0.0f, 0.0f, alpha);
409  Gizmos.DrawMesh(meshBorder, 0 );
410  }
411 
412  if(meshSurface != null)
413  {
414  Gizmos.color = new Color (0.5f, 0.5f, 0.5f, alpha);
415  Gizmos.DrawMesh(meshSurface, 0 );
416  }
417 
418  if(meshLogoBorder != null && meshLogoLeftCharacter != null && meshLogoRightCharacter != null)
419  {
420  DrawLogo();
421  }
422 
423  if(showGrid)
424  {
425  DrawGrid();
426  DrawRulers();
427  }
428 
429  Gizmos.matrix = oldMatrix;
430  Gizmos.color = oldColor;
431  }
432 
433  private void DrawLogo()
434  {
435  var logoDiameter = 2.5f; // The logo mesh is about 12cm when imported. 2.5cm is better.
436  var logoRadius = logoDiameter / 2f; // 1.25cm diameter logo
437  var borderThickness = new Length(GameBoard.GameboardExtents.BORDER_WIDTH_IN_METERS, LengthUnit.Meters).ToCentimeters;
438  var gameBoardFrontExtent = -gameBoardSettings.currentGameBoard.transform.forward / 2f;
439  var gameBoardRightExtent = gameBoardSettings.currentGameBoard.transform.right / 2f;
440 
441  // Starting in the front-right corner...
442  var logoPosition = (gameBoardRightExtent + gameBoardFrontExtent) * usableGameBoardWidthInMeters;
443  // ...move the logo left 5cm and center it on the game board border.
444  var oneCentimeterLengthInMeters = new Length(1, LengthUnit.Centimeters).ToMeters;
445  logoPosition -= gameBoardSettings.currentGameBoard.transform.right * 5f * oneCentimeterLengthInMeters;
446  logoPosition -= gameBoardSettings.currentGameBoard.transform.forward * ((borderThickness / 2) - logoRadius) * oneCentimeterLengthInMeters;
447 
448  var contentScaleFactor = scaleToUWRLD_UGBD;
449  Matrix4x4 mtxOrigin = Matrix4x4.TRS( Vector3.zero, Quaternion.identity, Vector3.one );
450  var mtxWorld = Matrix4x4.TRS( gameBoardSettings.gameBoardCenter + logoPosition * contentScaleFactor,
451  gameBoardSettings.currentGameBoard.rotation,
452  logoDiameter * Vector3.one * contentScaleFactor );
453  Matrix4x4 mtxPreTransform = Matrix4x4.TRS( Vector3.zero, Quaternion.Euler(-90.0f, 180.0f, 0.0f), Vector3.one );
454  Gizmos.matrix = mtxOrigin * mtxWorld * mtxPreTransform;
455 
456  Gizmos.color = new Color (0.969f, 0.969f, 0.969f, gizmoAlpha); // T5 Light Gray Background
457  Gizmos.DrawMesh( meshLogoBorder, 0 );
458 
459  Gizmos.color = new Color (0.945f, 0.349f, 0.133f, gizmoAlpha); // T5 Orange
460  Gizmos.DrawMesh( meshLogoLeftCharacter, 0 );
461 
462  Gizmos.color = new Color (0.376f, 0.392f, 0.439f, gizmoAlpha); // T5 Gray
463  Gizmos.DrawMesh( meshLogoRightCharacter, 0 );
464  }
465 
466  private void DrawGrid()
467  {
468  var contentScaleFactor = scaleToUWRLD_UGBD;
469  float heightOffsetInMeters = yGridOffset * scaleSettings.oneUnitLengthInMeters;
470  float lengthOffsetInMeters = Mathf.Max(usableGameBoardLengthInMeters - usableGameBoardWidthInMeters, 0f) / 2f;
471 
472  var gameBoardTransform = gameBoardSettings.currentGameBoard.transform;
473 
474  // Define the transformations for the grid origin and orientation.
475  Matrix4x4 mtxOrigin = Matrix4x4.TRS( Vector3.zero, Quaternion.identity, Vector3.one );
476  Matrix4x4 mtxPreTransform = Matrix4x4.TRS(
477  Vector3.zero,
478  Quaternion.Euler(0.0f, 0.0f, 0.0f),
479  Vector3.one * Mathf.Min(usableGameBoardWidthInMeters, usableGameBoardLengthInMeters));
480  //new Vector3(usableGameBoardWidthInMeters, 1f, usableGameBoardLengthInMeters));
481  Matrix4x4 mtxWorld = Matrix4x4.TRS(
482  gameBoardSettings.gameBoardCenter + (gameBoardTransform.up * heightOffsetInMeters / contentScaleFactor),
483  gameBoardTransform.rotation,
484  Vector3.one / contentScaleFactor );
485  Matrix4x4 mtxGizmo = mtxOrigin * mtxWorld * mtxPreTransform;
486  Gizmos.matrix = mtxGizmo;
487 
488  // Unless we're using inches, grid lines should appear in groups of 10.
489  var gridLinePeriod = scaleSettings.contentScaleUnit == LengthUnit.Inches ? 12 : 10;
490 
491  // We want to fade out grid lines spaced closer than 10 pixels on the screen.
492  // We'll do two iterations of fading out.
493  float beginFadeThreshold = 20f;
494  float endFadeThreshold = 10f;
495 
496  // To determine the spacing, we'll sample a position under the camera (even if it's off the board).
497  TryGetCameraPositionOverGameBoard(out var cameraPositionOverGameBoard);
498  Vector3 testUnitVector = cameraPositionOverGameBoard + Vector3.right * scaleSettings.oneUnitLengthInMeters / contentScaleFactor;
499  Vector3 secondTestUnitVector = cameraPositionOverGameBoard + Vector3.right * gridLinePeriod * scaleSettings.oneUnitLengthInMeters / contentScaleFactor;
500 
501  var screenSpaceDistance = (SceneView.currentDrawingSceneView.camera.WorldToScreenPoint(testUnitVector)
502  - SceneView.currentDrawingSceneView.camera.WorldToScreenPoint(cameraPositionOverGameBoard)).magnitude;
503  var secondScreenSpaceDistance = (SceneView.currentDrawingSceneView.camera.WorldToScreenPoint(secondTestUnitVector)
504  - SceneView.currentDrawingSceneView.camera.WorldToScreenPoint(cameraPositionOverGameBoard)).magnitude;
505 
506  // Check the sampled values against the thresholds we defined above and calculate transparency values.
507  var beginFade = screenSpaceDistance < beginFadeThreshold;
508  var endFade = screenSpaceDistance < endFadeThreshold;
509 
510  var beginSecondFade = secondScreenSpaceDistance < beginFadeThreshold;
511  var endSecondFade = secondScreenSpaceDistance < endFadeThreshold;
512 
513  var fadeFactor = 1f - Mathf.Clamp((beginFadeThreshold - screenSpaceDistance) / endFadeThreshold, 0f, 1f);
514  var secondFadeFactor = 1f - Mathf.Clamp((beginFadeThreshold - secondScreenSpaceDistance) / endFadeThreshold, 0f, 1f);
515 
516  var fadeAlpha = gizmoAlpha * fadeFactor;
517  var secondFadeAlpha = gizmoAlpha * secondFadeFactor;
518 
519  // Finally, draw some grid lines.
520  var oldColor = Gizmos.color;
521 
522  DrawGridLines(xGridLines, gridLinePeriod, beginFade, endFade, fadeAlpha, beginSecondFade, endSecondFade, secondFadeAlpha);
523  DrawGridLines(zGridLines, gridLinePeriod, beginFade, endFade, fadeAlpha, beginSecondFade, endSecondFade, secondFadeAlpha);
524 
525  Gizmos.color = oldColor;
526  }
527 
528  private void DrawGridLines(List<LineSegment> lines, int gridLinePeriod, bool beginFade, bool endFade,
529  float fadeAlpha, bool beginSecondFade, bool endSecondFade, float secondFadeAlpha)
530  {
531  var gridLinePeriodSquared = gridLinePeriod * gridLinePeriod;
532  var mtxGizmo = Gizmos.matrix;
533 
534  // We want to use a sphere around the projected cursor position to check for collision with lines on the grid.
535  // Any lines that collide will be colored differently. Set the sphere diameter to a single content unit, and scale it as the grid fades.
536  float cursorSphereRadius = scaleToUWRLD_UGBD * (scaleSettings.oneUnitLengthInMeters / 2f);
537  if(endSecondFade)
538  {
539  cursorSphereRadius *= (gridLinePeriod * gridLinePeriod) / 2f;
540  }
541  else if(endFade)
542  {
543  cursorSphereRadius *= gridLinePeriod / 2f;
544  }
545 
546  for(int i = 0; i < lines.Count; i++)
547  {
548  // Determine whether we should skip drawing this line.
549  var isPeriodic = i % gridLinePeriod == 0 || i % gridLinePeriod == gridLinePeriod - 1;
550  if(endFade && !isPeriodic) { continue; }
551 
552  var isDoublePeriodic = i % gridLinePeriodSquared == 0 || i % gridLinePeriodSquared == gridLinePeriodSquared - 1;
553  if(endSecondFade && !isDoublePeriodic) { continue; }
554 
555  // We'll need the current line defined in world space to check for cursor collision.
556  var currentGridLineSegment = lines[i];
557  var transformedLineSegment = new LineSegment(
558  mtxGizmo.MultiplyPoint(currentGridLineSegment.Start),
559  mtxGizmo.MultiplyPoint(currentGridLineSegment.End), currentGridLineSegment.LOD);
560 
561  var lineCollidesWithCursor = TryGetCursorPosition(out var cursorPosition)
562  && CheckRaySphereCollision(cursorPosition, cursorSphereRadius, transformedLineSegment);
563 
564  // Apply any fading required by the camera's distance from the grid.
565  var lineAlpha = gizmoAlpha;
566  if(beginSecondFade && !isDoublePeriodic)
567  {
568  lineAlpha = secondFadeAlpha;
569  }
570  else if(beginFade && !isPeriodic)
571  {
572  lineAlpha = fadeAlpha;
573  }
574 
575  Gizmos.color = lineCollidesWithCursor
576  ? new Color(1f, 0f, 0f, gizmoAlpha)
577  : new Color(1f, 1f, 1f, lineAlpha);
578 
579  // If the line collides with the cursor, project it on the edges of the board.
580  if(lineCollidesWithCursor)
581  {
582  var oldGizmoMatrix = Gizmos.matrix;
583  var xLineExtensionFactor = totalGameBoardWidthInMeters / usableGameBoardWidthInMeters;
584  var zLineExtensionFactor = totalGameBoardLengthInMeters / usableGameBoardLengthInMeters;
585 
586  // We need to know which direction to extend it. It's either x-axis aligned or z-axis aligned.
587  var lineDifference = currentGridLineSegment.End - currentGridLineSegment.Start;
588  var xAxisAligned = Mathf.Abs(Vector3.Dot(Vector3.right, lineDifference)) == 1;
589 
590  var mtxPreTransform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity,
591  (xAxisAligned ? new Vector3(xLineExtensionFactor, 1f, 1f) : new Vector3(1f, 1f, zLineExtensionFactor)));
592  var newMtxWorld = Matrix4x4.TRS(
593  gameBoardSettings.currentGameBoard.transform.position,
594  gameBoardSettings.currentGameBoard.transform.rotation,
595  new Vector3(scaleToUWRLD_UGBD * usableGameBoardWidthInMeters, 0f, scaleToUWRLD_UGBD * usableGameBoardWidthInMeters));
596  Gizmos.matrix = newMtxWorld;
597 
598  // Finally, draw a line.
599  Gizmos.DrawLine(currentGridLineSegment.Start, mtxPreTransform.MultiplyPoint(currentGridLineSegment.Start));
600  Gizmos.DrawLine(currentGridLineSegment.End, mtxPreTransform.MultiplyPoint(currentGridLineSegment.End));
601  Gizmos.matrix = oldGizmoMatrix;
602  }
603  Gizmos.DrawLine(currentGridLineSegment.Start, currentGridLineSegment.End);
604  }
605  }
606 
607 
608  private bool TryGetCursorPosition(out Vector3 intersectPoint)
609  {
610  var sceneView = UnityEditor.SceneView.currentDrawingSceneView;
611  var sceneViewCamera = sceneView.camera;
612  var mousePos = Event.current.mousePosition - sceneView.position.position;
613 
614  // Get the mouse position
615  mousePos = GUIUtility.GUIToScreenPoint(mousePos);
616 
617  // The calls above tends to include some extra invisible space above the rendered scene view, so we compensate.
618  var frameOffset = Vector2.down * 36f;
619  mousePos.y = (sceneViewCamera.pixelHeight - mousePos.y - frameOffset.y);
620 
621  var ray = sceneViewCamera.ScreenPointToRay(mousePos);
622 
623  return TryGetGridPlanePosition(ray, out intersectPoint);
624  }
625 
626  private bool TryGetCameraPositionOverGameBoard(out Vector3 intersectPoint)
627  {
628  var ray = new Ray(UnityEditor.SceneView.currentDrawingSceneView.camera.transform.position, -gameBoardSettings.currentGameBoard.transform.up);
629  return TryGetGridPlanePosition(ray, out intersectPoint);
630  }
631 
632  private bool TryGetGridPlanePosition(Ray ray, out Vector3 intersectPoint)
633  {
634  // Set up the collision plane
635  var planeOffsetY = (yGridOffset * scaleSettings.oneUnitLengthInMeters) / scaleToUWRLD_UGBD;
636  var gridPlane = new Plane(
637  gameBoardSettings.currentGameBoard.transform.up,
638  gameBoardSettings.currentGameBoard.transform.localToWorldMatrix.MultiplyPoint(planeOffsetY * Vector3.up));
639 
640  if(gridPlane.Raycast(ray, out var intersectionDistance))
641  {
642  // Get the point along the ray intersecting the plane.
643  intersectPoint = ray.GetPoint(intersectionDistance);
644  return true;
645  }
646 
647  intersectPoint = Vector3.zero;
648  return false;
649  }
650 
651  private bool CheckRaySphereCollision(Vector3 sphereCenter, float sphereRadius, LineSegment lineSegment){
652 
653  var ray = new Ray(lineSegment.Start, lineSegment.End - lineSegment.Start);
654 
655  Vector3 rayToSphereCenterDistance = ray.origin - sphereCenter;
656  float a = Vector3.Dot(ray.direction, ray.direction);
657  float b = 2f * Vector3.Dot(rayToSphereCenterDistance, ray.direction);
658  float c = Vector3.Dot(rayToSphereCenterDistance, rayToSphereCenterDistance) - sphereRadius * sphereRadius;
659  float discriminant = (b * b) - (4*a*c);
660  return discriminant > 0;
661  }
662 
663  private void DrawRulers()
664  {
665  if(meshRuler == null) { return; }
666 
667  Gizmos.color = new Color (1f, 0.8320962f, 0.3803922f, gizmoAlpha);
668 
669  var contentScaleFactor = scaleToUWRLD_UGBD;
670  float borderWidthInMeters = GameBoard.GameboardExtents.BORDER_WIDTH_IN_METERS;
671  float lengthOffsetInMeters = Mathf.Max(usableGameBoardLengthInMeters - usableGameBoardWidthInMeters, 0f) / 2f;
672  var gameBoardTransform = gameBoardSettings.currentGameBoard.transform;
673  var gameboardCenter = gameBoardSettings.gameBoardCenter;
674 
675  Matrix4x4 mtxOrigin = Matrix4x4.TRS( Vector3.zero, Quaternion.identity, Vector3.one );
676  Matrix4x4 mtxPreTransform = Matrix4x4.TRS( Vector3.zero, Quaternion.Euler(0.0f, 0.0f, 0.0f), new Vector3(usableGameBoardWidthInMeters, 1f, usableGameBoardLengthInMeters));
677 
678  // Far edge
679  Matrix4x4 mtxWorld = Matrix4x4.TRS(gameboardCenter + (gameBoardTransform.forward * lengthOffsetInMeters / contentScaleFactor) + (0.5f * (usableGameBoardLengthInMeters + borderWidthInMeters) * gameBoardTransform.forward) / contentScaleFactor,
680  Quaternion.Euler(gameBoardSettings.gameBoardRotation),
681  Vector3.one / contentScaleFactor );
682  Gizmos.matrix = mtxOrigin * mtxWorld * mtxPreTransform;
683  Gizmos.DrawMesh(meshRuler, 0 );
684 
685  // Near edge
686  mtxWorld = Matrix4x4.TRS(gameboardCenter - (0.5f * (usableGameBoardWidthInMeters + borderWidthInMeters) * gameBoardTransform.forward) / contentScaleFactor,
687  Quaternion.Euler(gameBoardSettings.gameBoardRotation),
688  Vector3.one / contentScaleFactor );
689  Gizmos.matrix = mtxOrigin * mtxWorld * mtxPreTransform;
690  Gizmos.DrawMesh(meshRuler, 0 );
691 
692  // Right edge
693  mtxPreTransform = Matrix4x4.TRS( Vector3.zero, Quaternion.Euler(0.0f, 90.0f, 0.0f), Vector3.one * usableGameBoardWidthInMeters);
694  mtxWorld = Matrix4x4.TRS(gameboardCenter + (0.5f * (usableGameBoardWidthInMeters + borderWidthInMeters) * gameBoardTransform.right) / contentScaleFactor,
695  Quaternion.Euler(gameBoardSettings.gameBoardRotation),
696  Vector3.one / contentScaleFactor );
697  Gizmos.matrix = mtxOrigin * mtxWorld * mtxPreTransform;
698  Gizmos.DrawMesh(meshRuler, 0 );
699 
700  // Left Edge
701  mtxWorld = Matrix4x4.TRS(gameboardCenter - (0.5f * (usableGameBoardWidthInMeters + borderWidthInMeters) * gameBoardTransform.right) / contentScaleFactor,
702  Quaternion.Euler(gameBoardSettings.gameBoardRotation),
703  Vector3.one / contentScaleFactor );
704  Gizmos.matrix = mtxOrigin * mtxWorld * mtxPreTransform;
705  Gizmos.DrawMesh(meshRuler, 0 );
706  }
707  }
708 }
709 #endif
Definition: Log.cs:21
LengthUnit
Definition: Length.cs:22
GameboardType
The type of Gameboard being tracked by the glasses