Skip to content

Commit 0011958

Browse files
authored
Merge pull request #424 from HodgsonSDAS/HTK-MaterialRefFixes
Fixes material memory leaks in various files
2 parents 003f673 + 1ceb1e1 commit 0011958

File tree

7 files changed

+102
-42
lines changed

7 files changed

+102
-42
lines changed

Assets/HoloToolkit/Input/Tests/Scripts/FocusedObjectColorChanger.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,34 @@ namespace HoloToolkit.Unity.InputModule.Tests
66
/// FocusedObjectMessageReceiver class shows how to handle focus events.
77
/// This particular implementatoin controls object appearance by changing its color when focused.
88
/// </summary>
9+
[RequireComponent(typeof(Renderer))]
910
public class FocusedObjectColorChanger : MonoBehaviour, IFocusable
1011
{
11-
[Tooltip("Object color changes to this when focused.")] public Color FocusedColor = Color.red;
12+
[Tooltip("Object color changes to this when focused.")]
13+
public Color FocusedColor = Color.red;
1214

13-
private Material material;
1415
private Color originalColor;
16+
private Material cachedMaterial;
1517

16-
private void Start()
18+
private void Awake()
1719
{
18-
material = GetComponent<Renderer>().material;
19-
originalColor = material.color;
20+
cachedMaterial = GetComponent<Renderer>().material;
21+
originalColor = cachedMaterial.GetColor("_Color");
2022
}
2123

2224
public void OnFocusEnter()
2325
{
24-
material.color = FocusedColor;
26+
cachedMaterial.SetColor("_Color", FocusedColor);
2527
}
2628

2729
public void OnFocusExit()
2830
{
29-
material.color = originalColor;
31+
cachedMaterial.SetColor("_Color", originalColor);
32+
}
33+
34+
private void OnDestroy()
35+
{
36+
DestroyImmediate(cachedMaterial);
3037
}
3138
}
3239
}

Assets/HoloToolkit/Input/Tests/Scripts/SelectedObjectMessageReceiver.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,34 @@ namespace HoloToolkit.Unity.InputModule.Tests
66
/// SelectedObjectMessageReceiver class shows how to handle messages sent by SelectedObjectMessageSender.
77
/// This particular implementatoin controls object appearance by changing its color when selected.
88
/// </summary>
9+
[RequireComponent(typeof(Renderer))]
910
public class SelectedObjectMessageReceiver : MonoBehaviour
1011
{
11-
[Tooltip("Object color changes to this when selected.")] public Color SelectedColor = Color.red;
12+
[Tooltip("Object color changes to this when selected.")]
13+
public Color SelectedColor = Color.red;
1214

13-
private Material material;
1415
private Color originalColor;
16+
private Material cachedMaterial;
1517

16-
private void Start()
18+
private void Awake()
1719
{
18-
material = GetComponent<Renderer>().material;
19-
originalColor = material.color;
20+
cachedMaterial = GetComponent<Renderer>().material;
21+
originalColor = cachedMaterial.GetColor("_Color");
2022
}
2123

2224
public void OnSelectObject()
2325
{
24-
material.color = SelectedColor;
26+
cachedMaterial.SetColor("_Color", SelectedColor);
2527
}
2628

2729
public void OnClearSelection()
2830
{
29-
material.color = originalColor;
31+
cachedMaterial.SetColor("_Color", originalColor);
32+
}
33+
34+
private void OnDestroy()
35+
{
36+
DestroyImmediate(cachedMaterial);
3037
}
3138
}
3239
}
Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
using System;
54
using UnityEngine;
65

76
namespace HoloToolkit.Unity.InputModule.Tests
87
{
98
public class SphereGlobalKeywords : MonoBehaviour, ISpeechHandler
109
{
10+
private Material[] cachedChildMaterials;
11+
12+
private void Awake()
13+
{
14+
Renderer[] childRenderers = GetComponentsInChildren<Renderer>();
15+
if (childRenderers != null && childRenderers.Length > 0)
16+
{
17+
cachedChildMaterials = new Material[childRenderers.Length];
18+
for (int i = 0; i < childRenderers.Length; i++)
19+
{
20+
cachedChildMaterials[i] = childRenderers[i].material;
21+
}
22+
}
23+
}
24+
1125
public void OnSpeechKeywordRecognized(SpeechKeywordRecognizedEventData eventData)
1226
{
1327
switch (eventData.RecognizedText.ToLower())
1428
{
1529
case "reset all":
16-
foreach (Renderer renderer in GetComponentsInChildren<Renderer>())
30+
foreach (Material cachedChildMaterial in cachedChildMaterials)
1731
{
18-
renderer.material.color = Color.gray;
32+
cachedChildMaterial.SetColor("_Color", Color.gray);
1933
}
2034
break;
2135
}
2236
}
37+
38+
private void OnDestroy()
39+
{
40+
for (int i = 0; i < cachedChildMaterials.Length; i++)
41+
{
42+
DestroyImmediate(cachedChildMaterials[i]);
43+
}
44+
}
2345
}
2446
}
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
using System;
54
using UnityEngine;
65

76
namespace HoloToolkit.Unity.InputModule.Tests
87
{
8+
[RequireComponent(typeof(Renderer))]
99
public class SphereKeywords : MonoBehaviour, ISpeechHandler
1010
{
11+
private Material cachedMaterial;
12+
13+
private void Awake()
14+
{
15+
cachedMaterial = GetComponent<Renderer>().material;
16+
}
17+
1118
public void ChangeColor(string color)
1219
{
1320
switch (color.ToLower())
1421
{
1522
case "red":
16-
GetComponent<Renderer>().material.color = Color.red;
23+
cachedMaterial.SetColor("_Color", Color.red);
1724
break;
1825
case "blue":
19-
GetComponent<Renderer>().material.color = Color.blue;
26+
cachedMaterial.SetColor("_Color", Color.blue);
2027
break;
2128
case "green":
22-
GetComponent<Renderer>().material.color = Color.green;
29+
cachedMaterial.SetColor("_Color", Color.green);
2330
break;
2431
}
2532
}
@@ -28,5 +35,10 @@ public void OnSpeechKeywordRecognized(SpeechKeywordRecognizedEventData eventData
2835
{
2936
ChangeColor(eventData.RecognizedText);
3037
}
38+
39+
private void OnDestroy()
40+
{
41+
DestroyImmediate(cachedMaterial);
42+
}
3143
}
3244
}

Assets/HoloToolkit/Input/Tests/Scripts/TestButton.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class TestButton : MonoBehaviour, IInputClickHandler, IFocusable
3333
public bool EnableActivation = true;
3434

3535
private AnimatorControllerParameter[] animatorHashes;
36+
private Material cachedToolTipMaterial;
3637

3738
private bool focused;
3839
public bool Focused
@@ -101,13 +102,14 @@ protected virtual void Awake()
101102

102103
protected virtual void OnEnable()
103104
{
104-
105105
// Set the initial alpha
106106
if (ToolTipRenderer != null)
107107
{
108-
Color tipColor = ToolTipRenderer.material.color;
108+
cachedToolTipMaterial = ToolTipRenderer.material;
109+
110+
Color tipColor = cachedToolTipMaterial.GetColor("_Color");
109111
tipColor.a = 0.0f;
110-
ToolTipRenderer.material.color = tipColor;
112+
cachedToolTipMaterial.SetColor("_Color", tipColor);
111113
toolTipTimer = 0.0f;
112114
}
113115

@@ -135,9 +137,9 @@ private void Update()
135137
// Update the new opacity
136138
if (ToolTipRenderer != null)
137139
{
138-
Color tipColor = ToolTipRenderer.material.color;
140+
Color tipColor = cachedToolTipMaterial.GetColor("_Color");
139141
tipColor.a = Mathf.Clamp(toolTipTimer, 0, ToolTipFadeTime) / ToolTipFadeTime;
140-
ToolTipRenderer.material.color = tipColor;
142+
cachedToolTipMaterial.SetColor("_Color", tipColor);
141143
}
142144
}
143145
}
@@ -229,5 +231,10 @@ public void OnFocusExit()
229231

230232
UpdateVisuals();
231233
}
234+
235+
private void OnDestroy()
236+
{
237+
DestroyImmediate(cachedToolTipMaterial);
238+
}
232239
}
233240
}

Assets/HoloToolkit/Sharing/Scripts/Utilities/Utils.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,16 @@ public static void SetMaterialRecursive(Transform t, Material mat)
112112
}
113113

114114
/// <summary>
115-
/// Change material for every object in hierarchy which has a name equal to nameToTest
115+
/// Change material for every object in hierarchy which has a name equal to nameToTest. WARNING!
116+
/// <see cref="http://answers.unity3d.com/questions/548420/material-memory-leak.html">See Community Answer</see>
117+
/// This function automatically instantiates the materials and makes them unique to this renderer.
118+
/// It is your responsibility to destroy the materials when the game object is being destroyed.
119+
/// Resources.UnloadUnusedAssets also destroys the materials but it is usually only called when loading a new level.
120+
/// <see cref="https://docs.unity3d.com/ScriptReference/Renderer-material.html">See Unity Documentation</see>
116121
/// </summary>
117122
/// <param name="t">root transform to start looking for renderers</param>
118123
/// <param name="mat">material to set everything to</param>
119-
/// <param name="ignoreName">ignore GameObjects with this name</param>
124+
/// <param name="nameToTest">ignore GameObjects with this name</param>
120125
public static void SetMaterialRecursiveForName(Transform t, Material mat, string nameToTest)
121126
{
122127
if (t.gameObject && t.gameObject.GetComponent<Renderer>() && t.gameObject.name == nameToTest)
@@ -128,6 +133,8 @@ public static void SetMaterialRecursiveForName(Transform t, Material mat, string
128133
{
129134
SetMaterialRecursiveForName(t.GetChild(ii), mat, nameToTest);
130135
}
136+
137+
Resources.UnloadUnusedAssets();
131138
}
132139

133140
/// <summary>

Assets/HoloToolkit/Utilities/Scripts/DirectionIndicator.cs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public class DirectionIndicator : MonoBehaviour
3434
// Cache the MeshRenderer for the on-cursor indicator since it will be enabled and disabled frequently.
3535
private Renderer directionIndicatorRenderer;
3636

37+
// Cache the Material to prevent material leak.
38+
private Material indicatorMaterial;
39+
3740
// Check if the cursor direction indicator is visible.
3841
private bool isDirectionIndicatorVisible;
3942

@@ -55,13 +58,13 @@ public void Awake()
5558
if (DirectionIndicatorObject == null)
5659
{
5760
Debug.LogError("Direction Indicator failed to instantiate.");
58-
return;
5961
}
6062
}
6163

6264
public void OnDestroy()
6365
{
64-
GameObject.Destroy(DirectionIndicatorObject);
66+
DestroyImmediate(indicatorMaterial);
67+
Destroy(DirectionIndicatorObject);
6568
}
6669

6770
private GameObject InstantiateDirectionIndicator(GameObject directionIndicator)
@@ -81,17 +84,17 @@ private GameObject InstantiateDirectionIndicator(GameObject directionIndicator)
8184
directionIndicatorRenderer.enabled = false;
8285

8386
// Remove any colliders and rigidbodies so the indicators do not interfere with Unity's physics system.
84-
foreach (Collider collider in indicator.GetComponents<Collider>())
87+
foreach (Collider indicatorCollider in indicator.GetComponents<Collider>())
8588
{
86-
Destroy(collider);
89+
Destroy(indicatorCollider);
8790
}
8891

8992
foreach (Rigidbody rigidBody in indicator.GetComponents<Rigidbody>())
9093
{
9194
Destroy(rigidBody);
9295
}
9396

94-
Material indicatorMaterial = directionIndicatorRenderer.material;
97+
indicatorMaterial = directionIndicatorRenderer.material;
9598
indicatorMaterial.color = DirectionIndicatorColor;
9699
indicatorMaterial.SetColor("_TintColor", DirectionIndicatorColor);
97100

@@ -132,14 +135,11 @@ private bool IsTargetVisible()
132135
// This will return true if the target's mesh is within the Main Camera's view frustums.
133136
Vector3 targetViewportPosition = Camera.main.WorldToViewportPoint(gameObject.transform.position);
134137
return (targetViewportPosition.x > VisibilitySafeFactor && targetViewportPosition.x < 1 - VisibilitySafeFactor &&
135-
targetViewportPosition.y > VisibilitySafeFactor && targetViewportPosition.y < 1 - VisibilitySafeFactor &&
136-
targetViewportPosition.z > 0);
138+
targetViewportPosition.y > VisibilitySafeFactor && targetViewportPosition.y < 1 - VisibilitySafeFactor &&
139+
targetViewportPosition.z > 0);
137140
}
138141

139-
private void GetDirectionIndicatorPositionAndRotation(
140-
Vector3 camToObjectDirection,
141-
out Vector3 position,
142-
out Quaternion rotation)
142+
private void GetDirectionIndicatorPositionAndRotation(Vector3 camToObjectDirection, out Vector3 position, out Quaternion rotation)
143143
{
144144
// Find position:
145145
// Save the cursor transform position in a variable.
@@ -160,9 +160,7 @@ private void GetDirectionIndicatorPositionAndRotation(
160160
position = origin + cursorIndicatorDirection * MetersFromCursor;
161161

162162
// Find the rotation from the facing direction to the target object.
163-
rotation = Quaternion.LookRotation(
164-
Camera.main.transform.forward,
165-
cursorIndicatorDirection) * directionIndicatorDefaultRotation;
163+
rotation = Quaternion.LookRotation(Camera.main.transform.forward, cursorIndicatorDirection) * directionIndicatorDefaultRotation;
166164
}
167165
}
168166
}

0 commit comments

Comments
 (0)