Tilt Five™ Unity API  1.3.0
 
Loading...
Searching...
No Matches
TableTopGizmo.cs
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020-2022 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
17using UnityEngine;
18
19#if UNITY_EDITOR
20using UnityEditor;
21using System.Collections.Generic;
22
23namespace TiltFive
24{
28 public class TableTopGizmo {
29
30 private string boardMeshAssetPath_LE = "Assets/Tilt Five/Meshes/Gameboard_LE.fbx";
31 private string surfaceMeshChildPath_LE = "Surface_LE";
32 private string borderMeshChildPath_LE = "Border_LE";
33
34 private string boardMeshAssetPath_XE = "Assets/Tilt Five/Meshes/Gameboard_XE.fbx";
35 private string surfaceMeshChildPath_XE = "Surface_Flat";
36 private string borderMeshChildPath_XE = "Border_Flat";
37
38 private string boardMeshAssetPath_XE_Raised = "Assets/Tilt Five/Meshes/Gameboard_XE_Raised.fbx";
39 private string surfaceMeshChildPath_XE_Raised = "Surface_Raised";
40 private string borderMeshChildPath_XE_Raised = "Border_Raised";
41
42 private string logoMeshAssetPath = "Assets/Tilt Five/Meshes/T5-Logo.fbx";
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.GameboardDimensions gameboardDimensions = new GameBoard.GameboardDimensions
52 {
53 playableSpaceX = new Length(0.7f, LengthUnit.Meters),
54 playableSpaceY = new Length(0.7f, LengthUnit.Meters),
55 borderWidth = new Length(0.05f, LengthUnit.Meters)
56 };
57 private float totalGameBoardWidthInMeters => gameboardDimensions.totalSpaceX.ToMeters;
58 private float totalGameBoardLengthInMeters => gameboardDimensions.totalSpaceY.ToMeters;
59 private float usableGameBoardWidthInMeters => gameboardDimensions.playableSpaceX.ToMeters;
60 private float usableGameBoardLengthInMeters => gameboardDimensions.playableSpaceY.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.GameboardDimensions gameboardDimensions)
106 {
107 this.scaleSettings = scaleSettings;
108 this.gameBoardSettings = gameBoardSettings;
109 this.gizmoAlpha = alpha;
110 this.yGridOffset = gridOffsetY;
111 this.gameboardDimensions = gameboardDimensions;
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(gameboardDimensions.totalSpaceX, "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, gameboardDimensions);
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 / 2; // 1.25cm diameter logo
437 var borderThickness = gameboardDimensions.borderWidth.ToCentimeters;
438 var gameBoardFrontExtent = -gameBoardSettings.currentGameBoard.transform.forward / 2;
439 var gameBoardRightExtent = gameBoardSettings.currentGameBoard.transform.right / 2;
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 * 5 * oneCentimeterLengthInMeters;
446 logoPosition -= gameBoardSettings.currentGameBoard.transform.forward * ((borderThickness / 2) - logoRadius) * oneCentimeterLengthInMeters;
447
448 var contentScaleFactor = scaleSettings.physicalMetersPerWorldSpaceUnit * gameBoardSettings.gameBoardScale;
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 = scaleSettings.physicalMetersPerWorldSpaceUnit * gameBoardSettings.gameBoardScale;
469 float heightOffsetInMeters = yGridOffset * scaleSettings.oneUnitLengthInMeters;
470 float lengthOffsetInMeters = Mathf.Max(gameboardDimensions.playableSpaceY.ToMeters - gameboardDimensions.playableSpaceX.ToMeters, 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) / (scaleSettings.physicalMetersPerWorldSpaceUnit * gameBoardSettings.gameBoardScale);
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 = scaleSettings.physicalMetersPerWorldSpaceUnit * gameBoardSettings.gameBoardScale;
670 float borderWidthInMeters = gameboardDimensions.borderWidth.ToMeters;
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.