2026-04-24 버그수정 및 대사에 맞춘 캐릭터 동작
This commit is contained in:
4371
Assets/HighlightPlus/Runtime/Scripts/HighlightEffect.cs
Normal file
4371
Assets/HighlightPlus/Runtime/Scripts/HighlightEffect.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/HighlightPlus/Runtime/Scripts/HighlightEffect.cs.meta
Normal file
12
Assets/HighlightPlus/Runtime/Scripts/HighlightEffect.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 888380afc233049ce9e618f9f36c8ba8
|
||||
timeCreated: 1545593776
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 900
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
201
Assets/HighlightPlus/Runtime/Scripts/HighlightEffectActions.cs
Normal file
201
Assets/HighlightPlus/Runtime/Scripts/HighlightEffectActions.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public enum HitFxMode {
|
||||
Overlay = 0,
|
||||
InnerGlow = 1,
|
||||
LocalHit = 2
|
||||
}
|
||||
|
||||
public enum HitFXTriggerMode {
|
||||
Scripting = 0,
|
||||
WhenHighlighted = 10
|
||||
}
|
||||
|
||||
public partial class HighlightEffect : MonoBehaviour {
|
||||
|
||||
public static bool useUnscaledTime;
|
||||
|
||||
[NonSerialized]
|
||||
public Transform currentHitTarget;
|
||||
[NonSerialized]
|
||||
public Vector3 currentHitLocalPosition;
|
||||
[NonSerialized]
|
||||
public Vector3 currentHitNormal;
|
||||
|
||||
public static float GetTime () {
|
||||
return useUnscaledTime ? Time.unscaledTime : Time.time;
|
||||
}
|
||||
|
||||
#region Hit FX handling
|
||||
|
||||
[Range(0, 1)] public float hitFxInitialIntensity;
|
||||
public HitFxMode hitFxMode = HitFxMode.Overlay;
|
||||
public HitFXTriggerMode hitFXTriggerMode = HitFXTriggerMode.Scripting;
|
||||
public float hitFxFadeOutDuration = 0.25f;
|
||||
[ColorUsage(true, true)] public Color hitFxColor = Color.white;
|
||||
public float hitFxRadius = 0.5f;
|
||||
|
||||
float hitInitialIntensity;
|
||||
float hitStartTime;
|
||||
float hitFadeOutDuration;
|
||||
Color hitColor;
|
||||
bool hitActive;
|
||||
Vector3 hitPosition;
|
||||
float hitRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Performs a hit effect using default values
|
||||
/// </summary>
|
||||
public void HitFX () {
|
||||
HitFX(hitFxColor, hitFxFadeOutDuration, hitFxInitialIntensity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a hit effect localized at hit position and radius with default values
|
||||
/// </summary>
|
||||
public void HitFX (Vector3 position) {
|
||||
HitFX(hitFxColor, hitFxFadeOutDuration, hitFxInitialIntensity, position, hitFxRadius);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a hit effect using desired color, fade out duration and optionally initial intensity (0-1)
|
||||
/// </summary>
|
||||
public void HitFX (Color color, float fadeOutDuration, float initialIntensity = 1f) {
|
||||
hitInitialIntensity = initialIntensity;
|
||||
hitFadeOutDuration = fadeOutDuration;
|
||||
hitColor = color;
|
||||
hitStartTime = GetTime();
|
||||
hitActive = true;
|
||||
if (overlay == 0) {
|
||||
UpdateMaterialProperties();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Performs a hit effect using desired color, fade out duration, initial intensity (0-1), hit position and radius of effect
|
||||
/// </summary>
|
||||
public void HitFX (Color color, float fadeOutDuration, float initialIntensity, Vector3 position, float radius) {
|
||||
hitInitialIntensity = initialIntensity;
|
||||
hitFadeOutDuration = fadeOutDuration;
|
||||
hitColor = color;
|
||||
hitStartTime = GetTime();
|
||||
hitActive = true;
|
||||
hitPosition = position;
|
||||
hitRadius = radius;
|
||||
if (overlay == 0) {
|
||||
UpdateMaterialProperties();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the target FX on demand using predefined configuration (see targetFX... properties)
|
||||
/// </summary>
|
||||
public void TargetFX () {
|
||||
targetFXStartTime = GetTime();
|
||||
if (!_highlighted) {
|
||||
highlighted = true;
|
||||
}
|
||||
if (!targetFX) {
|
||||
targetFX = true;
|
||||
UpdateMaterialProperties();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the icon FX on demand using predefined configuration (see iconFX... properties)
|
||||
/// </summary>
|
||||
public void IconFX () {
|
||||
iconFXStartTime = GetTime();
|
||||
if (!_highlighted) {
|
||||
highlighted = true;
|
||||
}
|
||||
if (!iconFX) {
|
||||
iconFX = true;
|
||||
UpdateMaterialProperties();
|
||||
}
|
||||
}
|
||||
|
||||
#region Label handling
|
||||
|
||||
[NonSerialized]
|
||||
public HighlightLabel label;
|
||||
|
||||
void ReleaseLabel () {
|
||||
label?.Release();
|
||||
label = null;
|
||||
}
|
||||
|
||||
|
||||
void CheckLabel (bool disabling) {
|
||||
// Enable label
|
||||
bool shouldShowLabel = labelMode == LabelMode.Always || (_highlighted && labelMode == LabelMode.WhenHighlighted) || (labelShowInEditorMode && !Application.isPlaying);
|
||||
shouldShowLabel = shouldShowLabel && !disabling && rmsCount != 0;
|
||||
if (shouldShowLabel && labelEnabled) {
|
||||
// Lazy initialization of label prefab
|
||||
if (label == null && labelPrefab != null) {
|
||||
label = HighlightLabelPoolManager.GetLabelInstance(labelPrefab);
|
||||
}
|
||||
if (label != null) {
|
||||
label.ConfigureCanvas(labelRenderMode == LabelRenderMode.WorldSpace);
|
||||
label.textLabel = labelText;
|
||||
label.textColor = labelColor;
|
||||
label.textSize = labelTextSize;
|
||||
label.width = labelLineLength;
|
||||
label.labelAlignment = labelAlignment;
|
||||
label.labelRelativeAlignment = labelRelativeAlignment;
|
||||
label.labelAlignmentTransform = labelAlignmentTransform;
|
||||
label.labelMaxDistance = labelMaxDistance;
|
||||
label.labelFadeStartDistance = labelFadeStartDistance;
|
||||
label.labelScaleByDistance = labelScaleByDistance;
|
||||
label.labelScaleMin = labelScaleMin;
|
||||
label.labelScaleMax = labelScaleMax;
|
||||
label.worldSpaceScale = labelWorldSpaceScale;
|
||||
if (!label.isVisible) {
|
||||
label.SetPosition(labelTarget == null ? target : labelTarget, Misc.vector3Zero, new Vector3(0, labelVerticalOffset, 0), labelViewportOffset);
|
||||
label.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!labelEnabled) {
|
||||
ReleaseLabel();
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) {
|
||||
HighlightLabelPoolManager.DestroySceneLabels();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (label != null) {
|
||||
label.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHitPosition (Transform target, Vector3 localPosition, Vector3 offsetWS, Vector3 normalWS) {
|
||||
if (labelEnabled && labelFollowCursor && label != null) {
|
||||
label.SetPosition(target, localPosition, offsetWS);
|
||||
}
|
||||
currentHitTarget = target;
|
||||
currentHitLocalPosition = localPosition;
|
||||
currentHitNormal = normalWS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recreates label
|
||||
/// </summary>
|
||||
public void RefreshLabel () {
|
||||
HighlightLabelPoolManager.DestroySceneLabels();
|
||||
CheckLabel(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e749f80d0d29a49d49d6e0f4752065cd
|
||||
timeCreated: 1542876337
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,90 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
|
||||
public enum BlockerTargetOptions {
|
||||
OnlyThisObject,
|
||||
Children,
|
||||
LayerInChildren
|
||||
}
|
||||
|
||||
[DefaultExecutionOrder(100)]
|
||||
[ExecuteInEditMode]
|
||||
public class HighlightEffectBlocker : MonoBehaviour {
|
||||
|
||||
public BlockerTargetOptions include = BlockerTargetOptions.OnlyThisObject;
|
||||
public LayerMask layerMask = -1;
|
||||
public string nameFilter;
|
||||
public bool useRegEx;
|
||||
|
||||
public bool blockOutlineAndGlow = true;
|
||||
public bool blockOverlay = true;
|
||||
|
||||
List<Renderer> renderers;
|
||||
|
||||
void OnEnable () {
|
||||
Refresh();
|
||||
HighlightPlusRenderPassFeature.RegisterBlocker(this);
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
HighlightPlusRenderPassFeature.UnregisterBlocker(this);
|
||||
}
|
||||
|
||||
public void Refresh() {
|
||||
if (renderers == null) {
|
||||
renderers = new List<Renderer>();
|
||||
} else {
|
||||
renderers.Clear();
|
||||
}
|
||||
switch (include) {
|
||||
case BlockerTargetOptions.OnlyThisObject:
|
||||
Renderer r = GetComponent<Renderer>();
|
||||
if (r != null) renderers.Add(r);
|
||||
return;
|
||||
case BlockerTargetOptions.Children:
|
||||
GetComponentsInChildren<Renderer>(true, renderers);
|
||||
break;
|
||||
case BlockerTargetOptions.LayerInChildren:
|
||||
Renderer[] childRenderers = GetComponentsInChildren<Renderer>(true);
|
||||
for (int k = 0; k < childRenderers.Length; k++) {
|
||||
Renderer cr = childRenderers[k];
|
||||
if (cr != null && ((1 << cr.gameObject.layer) & layerMask) != 0) {
|
||||
renderers.Add(cr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(nameFilter)) {
|
||||
for (int k = renderers.Count - 1; k >= 0; k--) {
|
||||
string objName = renderers[k].name;
|
||||
if (useRegEx) {
|
||||
if (!System.Text.RegularExpressions.Regex.IsMatch(objName, nameFilter)) {
|
||||
renderers.RemoveAt(k);
|
||||
}
|
||||
} else if (!objName.Contains(nameFilter)) {
|
||||
renderers.RemoveAt(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void BuildCommandBuffer (CommandBuffer cmd, Material mat) {
|
||||
if (renderers == null) return;
|
||||
int renderersCount = renderers.Count;
|
||||
for (int k=0;k<renderersCount;k++) {
|
||||
Renderer r = renderers[k];
|
||||
if (r == null || !r.enabled || !r.gameObject.activeInHierarchy) continue;
|
||||
int submeshCount = r.sharedMaterials.Length;
|
||||
for (int i = 0; i < submeshCount; i++) {
|
||||
cmd.DrawRenderer(r, mat, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1449eaee17884bf3ad10a841cab981b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
|
||||
public partial class HighlightEffect : MonoBehaviour {
|
||||
|
||||
static readonly List<HighlightSeeThroughOccluder> occluders = new List<HighlightSeeThroughOccluder>();
|
||||
static readonly Dictionary<Camera, int> occludersFrameCount = new Dictionary<Camera, int>();
|
||||
static Material fxMatSeeThroughOccluder, fxMatDepthWrite;
|
||||
static RaycastHit[] hits;
|
||||
|
||||
/// <summary>
|
||||
/// True if the see-through is cancelled by an occluder using raycast method
|
||||
/// </summary>
|
||||
public bool IsSeeThroughOccluded(Camera cam) {
|
||||
|
||||
if (rms == null) return false;
|
||||
|
||||
// Compute bounds
|
||||
Bounds bounds = new Bounds();
|
||||
for (int r = 0; r < rms.Length; r++) {
|
||||
if (rms[r].renderer != null) {
|
||||
if (bounds.size.x == 0) {
|
||||
bounds = rms[r].renderer.bounds;
|
||||
} else {
|
||||
bounds.Encapsulate(rms[r].renderer.bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector3 pos = bounds.center;
|
||||
Vector3 camPos = cam.transform.position;
|
||||
Vector3 offset = pos - camPos;
|
||||
float maxDistance = Vector3.Distance(pos, camPos);
|
||||
if (hits == null || hits.Length == 0) {
|
||||
hits = new RaycastHit[64];
|
||||
}
|
||||
int occludersCount = occluders.Count;
|
||||
int hitCount = Physics.BoxCastNonAlloc(pos - offset, bounds.extents * 0.9f, offset.normalized, hits, Quaternion.identity, maxDistance);
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
for (int j = 0; j < occludersCount; j++) {
|
||||
if (hits[k].collider.transform == occluders[j].transform) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void RegisterOccluder(HighlightSeeThroughOccluder occluder) {
|
||||
if (!occluders.Contains(occluder)) {
|
||||
occluders.Add(occluder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterOccluder(HighlightSeeThroughOccluder occluder) {
|
||||
if (occluders.Contains(occluder)) {
|
||||
occluders.Remove(occluder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test see-through occluders.
|
||||
/// </summary>
|
||||
/// <param name="cam">The camera to be tested</param>
|
||||
/// <returns>Returns true if there's no raycast-based occluder cancelling the see-through effect</returns>
|
||||
public bool RenderSeeThroughOccluders(CommandBuffer cb, Camera cam) {
|
||||
|
||||
int occludersCount = occluders.Count;
|
||||
if (occludersCount == 0 || rmsCount == 0) return true;
|
||||
|
||||
bool useRayCastCheck = false;
|
||||
// Check if raycast method is needed
|
||||
for (int k = 0; k < occludersCount; k++) {
|
||||
HighlightSeeThroughOccluder occluder = occluders[k];
|
||||
if (occluder == null || !occluder.isActiveAndEnabled) continue;
|
||||
if (occluder.mode == OccluderMode.BlocksSeeThrough && occluder.detectionMethod == DetectionMethod.RayCast) {
|
||||
useRayCastCheck = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (useRayCastCheck) {
|
||||
if (IsSeeThroughOccluded(cam)) return false;
|
||||
}
|
||||
|
||||
// do not render see-through occluders more than once this frame per camera (there can be many highlight effect scripts in the scene, we only need writing to stencil once)
|
||||
int lastFrameCount;
|
||||
occludersFrameCount.TryGetValue(cam, out lastFrameCount);
|
||||
int currentFrameCount = Time.frameCount;
|
||||
if (currentFrameCount == lastFrameCount) return true;
|
||||
occludersFrameCount[cam] = currentFrameCount;
|
||||
|
||||
if (fxMatSeeThroughOccluder == null) {
|
||||
InitMaterial(ref fxMatSeeThroughOccluder, ref shaderSeeThroughOccluder, "HighlightPlus/Geometry/SeeThroughOccluder");
|
||||
if (fxMatSeeThroughOccluder == null) return true;
|
||||
}
|
||||
if (fxMatDepthWrite == null) {
|
||||
InitMaterial(ref fxMatDepthWrite, ref shaderDepthWrite, "HighlightPlus/Geometry/JustDepth");
|
||||
if (fxMatDepthWrite == null) return true;
|
||||
}
|
||||
|
||||
for (int k = 0; k < occludersCount; k++) {
|
||||
HighlightSeeThroughOccluder occluder = occluders[k];
|
||||
if (occluder == null || !occluder.isActiveAndEnabled) continue;
|
||||
if (occluder.detectionMethod == DetectionMethod.Stencil) {
|
||||
if (occluder.meshData == null) continue;
|
||||
int meshDataLength = occluder.meshData.Length;
|
||||
// Per renderer
|
||||
for (int m = 0; m < meshDataLength; m++) {
|
||||
// Per submesh
|
||||
Renderer renderer = occluder.meshData[m].renderer;
|
||||
if (renderer.isVisible) {
|
||||
for (int s = 0; s < occluder.meshData[m].subMeshCount; s++) {
|
||||
cb.DrawRenderer(renderer, occluder.mode == OccluderMode.BlocksSeeThrough ? fxMatSeeThroughOccluder : fxMatDepthWrite, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckOcclusion(Camera cam) {
|
||||
|
||||
if (!perCameraOcclusionData.TryGetValue(cam, out PerCameraOcclusionData occlusionData)) {
|
||||
occlusionData = new PerCameraOcclusionData();
|
||||
perCameraOcclusionData[cam] = occlusionData;
|
||||
}
|
||||
|
||||
float now = GetTime();
|
||||
int frameCount = Time.frameCount; // ensure all cameras are checked this frame
|
||||
|
||||
if (now - occlusionData.checkLastTime < seeThroughOccluderCheckInterval && Application.isPlaying && occlusionData.occlusionRenderFrame != frameCount) return occlusionData.lastOcclusionTestResult;
|
||||
|
||||
occlusionData.cachedOccluders.Clear();
|
||||
occlusionData.cachedOccluderCollider = null;
|
||||
|
||||
if (rms == null || rms.Length == 0 || rms[0].renderer == null) return false;
|
||||
|
||||
occlusionData.checkLastTime = now;
|
||||
occlusionData.occlusionRenderFrame = frameCount;
|
||||
|
||||
Vector3 camPos = cam.transform.position;
|
||||
|
||||
if (seeThroughOccluderCheckIndividualObjects) {
|
||||
for (int r = 0; r < rms.Length; r++) {
|
||||
if (rms[r].renderer != null) {
|
||||
Bounds bounds = rms[r].renderer.bounds;
|
||||
Vector3 pos = bounds.center;
|
||||
float maxDistance = Vector3.Distance(pos, camPos);
|
||||
if (Physics.BoxCast(pos, bounds.extents * seeThroughOccluderThreshold, (camPos - pos).normalized, out RaycastHit hitInfo, Quaternion.identity, maxDistance, seeThroughOccluderMask)) {
|
||||
occlusionData.cachedOccluderCollider = hitInfo.collider;
|
||||
occlusionData.lastOcclusionTestResult = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
occlusionData.lastOcclusionTestResult = false;
|
||||
return false;
|
||||
} else {
|
||||
// Compute combined bounds
|
||||
Bounds bounds = rms[0].renderer.bounds;
|
||||
for (int r = 1; r < rms.Length; r++) {
|
||||
if (rms[r].renderer != null) {
|
||||
bounds.Encapsulate(rms[r].renderer.bounds);
|
||||
}
|
||||
}
|
||||
Vector3 pos = bounds.center;
|
||||
float maxDistance = Vector3.Distance(pos, camPos);
|
||||
occlusionData.lastOcclusionTestResult = Physics.BoxCast(pos, bounds.extents * seeThroughOccluderThreshold, (camPos - pos).normalized, out RaycastHit hitInfo, Quaternion.identity, maxDistance, seeThroughOccluderMask);
|
||||
occlusionData.cachedOccluderCollider = hitInfo.collider;
|
||||
return occlusionData.lastOcclusionTestResult;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int MAX_OCCLUDER_HITS = 50;
|
||||
static RaycastHit[] occluderHits;
|
||||
|
||||
void AddWithoutRepetition(List<Renderer> target, List<Renderer> source, LayerMask layerMask) {
|
||||
int sourceCount = source.Count;
|
||||
for (int k = 0; k < sourceCount; k++) {
|
||||
Renderer entry = source[k];
|
||||
if (entry != null && !target.Contains(entry) && ValidRenderer(entry) && ((1<<entry.gameObject.layer) & layerMask) != 0) {
|
||||
target.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOcclusionAccurate(CommandBuffer cbuf, Camera cam) {
|
||||
|
||||
if (!perCameraOcclusionData.TryGetValue(cam, out PerCameraOcclusionData occlusionData)) {
|
||||
occlusionData = new PerCameraOcclusionData();
|
||||
perCameraOcclusionData[cam] = occlusionData;
|
||||
}
|
||||
|
||||
float now = GetTime();
|
||||
int frameCount = Time.frameCount; // ensure all cameras are checked this frame
|
||||
bool reuse = now - occlusionData.checkLastTime < seeThroughOccluderCheckInterval && Application.isPlaying && occlusionData.occlusionRenderFrame != frameCount;
|
||||
|
||||
if (!reuse) {
|
||||
|
||||
occlusionData.cachedOccluders.Clear();
|
||||
occlusionData.cachedOccluderCollider = null;
|
||||
|
||||
if (rms == null || rms.Length == 0 || rms[0].renderer == null) return;
|
||||
|
||||
occlusionData.checkLastTime = now;
|
||||
occlusionData.occlusionRenderFrame = frameCount;
|
||||
Quaternion quaternionIdentity = Quaternion.identity;
|
||||
Vector3 camPos = cam.transform.position;
|
||||
|
||||
if (occluderHits == null || occluderHits.Length < MAX_OCCLUDER_HITS) {
|
||||
occluderHits = new RaycastHit[MAX_OCCLUDER_HITS];
|
||||
}
|
||||
|
||||
if (seeThroughOccluderCheckIndividualObjects) {
|
||||
for (int r = 0; r < rms.Length; r++) {
|
||||
if (rms[r].renderer != null) {
|
||||
Bounds bounds = rms[r].renderer.bounds;
|
||||
Vector3 pos = bounds.center;
|
||||
float maxDistance = Vector3.Distance(pos, camPos);
|
||||
int numOccluderHits = Physics.BoxCastNonAlloc(pos, bounds.extents * seeThroughOccluderThreshold, (camPos - pos).normalized, occluderHits, quaternionIdentity, maxDistance, seeThroughOccluderMask);
|
||||
for (int k = 0; k < numOccluderHits; k++) {
|
||||
occluderHits[k].collider.transform.root.GetComponentsInChildren(tempRR);
|
||||
AddWithoutRepetition(occlusionData.cachedOccluders, tempRR, seeThroughOccluderMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Compute combined bounds
|
||||
Bounds bounds = rms[0].renderer.bounds;
|
||||
for (int r = 1; r < rms.Length; r++) {
|
||||
if (rms[r].renderer != null) {
|
||||
bounds.Encapsulate(rms[r].renderer.bounds);
|
||||
}
|
||||
}
|
||||
Vector3 pos = bounds.center;
|
||||
float maxDistance = Vector3.Distance(pos, camPos);
|
||||
int numOccluderHits = Physics.BoxCastNonAlloc(pos, bounds.extents * seeThroughOccluderThreshold, (camPos - pos).normalized, occluderHits, quaternionIdentity, maxDistance, seeThroughOccluderMask);
|
||||
for (int k = 0; k < numOccluderHits; k++) {
|
||||
occluderHits[k].collider.transform.root.GetComponentsInChildren(tempRR);
|
||||
AddWithoutRepetition(occlusionData.cachedOccluders, tempRR, seeThroughOccluderMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render occluders
|
||||
int occluderRenderersCount = occlusionData.cachedOccluders.Count;
|
||||
if (occluderRenderersCount > 0) {
|
||||
for (int k = 0; k < occluderRenderersCount; k++) {
|
||||
Renderer r = occlusionData.cachedOccluders[k];
|
||||
if (r != null) {
|
||||
cbuf.DrawRenderer(r, fxMatSeeThroughMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetOccluders(Camera camera, List<Transform> occluders) {
|
||||
occluders.Clear();
|
||||
if (perCameraOcclusionData.TryGetValue(camera, out PerCameraOcclusionData occlusionData)) {
|
||||
if (occlusionData.cachedOccluderCollider != null) {
|
||||
occluders.Add(occlusionData.cachedOccluderCollider.transform);
|
||||
return;
|
||||
}
|
||||
foreach (Renderer r in occlusionData.cachedOccluders) {
|
||||
if (r != null) {
|
||||
occluders.Add(r.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0bc1f04199a64e66ae9630062b3a6ad
|
||||
timeCreated: 1542876337
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
330
Assets/HighlightPlus/Runtime/Scripts/HighlightLabel.cs
Normal file
330
Assets/HighlightPlus/Runtime/Scripts/HighlightLabel.cs
Normal file
@@ -0,0 +1,330 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
[ExecuteAlways]
|
||||
public partial class HighlightLabel : MonoBehaviour {
|
||||
|
||||
public Camera cam;
|
||||
|
||||
[NonSerialized]
|
||||
public Transform target;
|
||||
[NonSerialized]
|
||||
public Vector3 localPosition;
|
||||
[NonSerialized]
|
||||
public Vector3 worldOffset;
|
||||
|
||||
[NonSerialized]
|
||||
public Vector2 viewportOffset;
|
||||
|
||||
[NonSerialized]
|
||||
public bool isVisible;
|
||||
|
||||
[NonSerialized]
|
||||
public LabelAlignment labelAlignment = LabelAlignment.Auto;
|
||||
|
||||
[NonSerialized]
|
||||
public bool labelRelativeAlignment;
|
||||
[NonSerialized]
|
||||
public Transform labelAlignmentTransform;
|
||||
|
||||
public GameObject labelPrefab;
|
||||
|
||||
internal bool isPooled;
|
||||
|
||||
[NonSerialized]
|
||||
public bool isWorldSpace;
|
||||
|
||||
[NonSerialized]
|
||||
public float worldSpaceScale = 0.01f;
|
||||
|
||||
static Material uiAlwaysOnTopMaterial;
|
||||
static int zTestModeID;
|
||||
|
||||
Canvas canvas;
|
||||
Image line;
|
||||
TextMeshProUGUI text;
|
||||
RectTransform panel;
|
||||
CanvasGroup canvasGroup;
|
||||
|
||||
[NonSerialized]
|
||||
public float labelMaxDistance = 250f;
|
||||
[NonSerialized]
|
||||
public float labelFadeStartDistance = 200f;
|
||||
[NonSerialized]
|
||||
public bool labelScaleByDistance;
|
||||
[NonSerialized]
|
||||
public float labelScaleMin = 1f;
|
||||
[NonSerialized]
|
||||
public float labelScaleMax = 1f;
|
||||
|
||||
[NonSerialized]
|
||||
public float highlightAlpha = 1f;
|
||||
|
||||
public virtual float alpha {
|
||||
get {
|
||||
return highlightAlpha;
|
||||
}
|
||||
set {
|
||||
highlightAlpha = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Color textColor {
|
||||
get {
|
||||
return text?.color ?? Color.white;
|
||||
}
|
||||
set {
|
||||
if (text != null) {
|
||||
text.color = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string textLabel {
|
||||
get {
|
||||
return text?.text ?? "";
|
||||
}
|
||||
set {
|
||||
if (text != null) {
|
||||
text.text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float textSize {
|
||||
get {
|
||||
return text?.fontSize ?? 14;
|
||||
}
|
||||
set {
|
||||
if (text != null) {
|
||||
text.fontSize = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual float width {
|
||||
get {
|
||||
return text?.rectTransform.sizeDelta.x ?? 200;
|
||||
}
|
||||
set {
|
||||
if (text == null) return;
|
||||
if (text.rectTransform != null) {
|
||||
Vector2 currentSize = text.rectTransform.sizeDelta;
|
||||
if (currentSize.x != value) {
|
||||
text.rectTransform.sizeDelta = new Vector2(value, currentSize.y);
|
||||
}
|
||||
}
|
||||
RectTransform panelRectTransform = panel.GetComponent<RectTransform>();
|
||||
if (panelRectTransform != null) {
|
||||
Vector2 currentSize = panelRectTransform.sizeDelta;
|
||||
if (currentSize.x != value) {
|
||||
panelRectTransform.sizeDelta = new Vector2(value, currentSize.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Awake () {
|
||||
canvas = GetComponent<Canvas>();
|
||||
line = GetComponentInChildren<Image>();
|
||||
text = GetComponentInChildren<TextMeshProUGUI>();
|
||||
panel = transform.GetChild(0).GetComponentInChildren<RectTransform>();
|
||||
canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (zTestModeID == 0) {
|
||||
zTestModeID = Shader.PropertyToID("_ZTestMode");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the label Canvas render mode for screen space or world space rendering.
|
||||
/// </summary>
|
||||
public void ConfigureCanvas(bool worldSpace) {
|
||||
if (isWorldSpace == worldSpace) return;
|
||||
isWorldSpace = worldSpace;
|
||||
if (canvas == null) return;
|
||||
if (worldSpace) {
|
||||
canvas.renderMode = RenderMode.WorldSpace;
|
||||
canvas.worldCamera = cam;
|
||||
SetAlwaysOnTop(true);
|
||||
} else {
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
SetAlwaysOnTop(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAlwaysOnTop(bool enabled) {
|
||||
// UI Image (the connecting line)
|
||||
if (line != null) {
|
||||
if (enabled) {
|
||||
if (uiAlwaysOnTopMaterial == null) {
|
||||
uiAlwaysOnTopMaterial = new Material(Shader.Find("UI/Default"));
|
||||
uiAlwaysOnTopMaterial.SetFloat(ShaderParams.ZTest, (float)CompareFunction.Always);
|
||||
}
|
||||
line.material = uiAlwaysOnTopMaterial;
|
||||
} else {
|
||||
line.material = null;
|
||||
}
|
||||
}
|
||||
|
||||
// TextMeshPro text
|
||||
if (text != null) {
|
||||
text.fontMaterial.SetFloat(zTestModeID, (float)(enabled ? CompareFunction.Always : CompareFunction.LessEqual));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the label to the pool
|
||||
/// </summary>
|
||||
public virtual void ReturnToPool () {
|
||||
if (!isPooled) return;
|
||||
gameObject.SetActive(false);
|
||||
HighlightLabelPoolManager.ReturnToPool(this);
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetPosition(Transform target, Vector3 localPosition, Vector3 worldOffset, Vector2 viewportOffset = default) {
|
||||
this.target = target;
|
||||
this.localPosition = localPosition;
|
||||
this.worldOffset = worldOffset;
|
||||
this.viewportOffset = viewportOffset;
|
||||
}
|
||||
|
||||
public virtual void UpdatePosition () {
|
||||
if (panel == null || text == null) return;
|
||||
if (cam == null) {
|
||||
cam = Camera.main;
|
||||
if (cam == null) return;
|
||||
}
|
||||
if (target == null) return;
|
||||
|
||||
Vector3 worldPosition = target.TransformPoint(localPosition);
|
||||
Vector3 worldWithOffset = worldPosition + worldOffset;
|
||||
|
||||
// Distance-based visibility & scaling (common to both modes)
|
||||
float distanceToCam = Vector3.Distance(cam.transform.position, worldWithOffset);
|
||||
float visibleAlpha = 1f;
|
||||
if (labelMaxDistance > 0f && distanceToCam > labelMaxDistance) {
|
||||
if (canvasGroup != null) canvasGroup.alpha = 0f;
|
||||
return;
|
||||
}
|
||||
if (labelFadeStartDistance > 0f && labelMaxDistance > labelFadeStartDistance && distanceToCam > labelFadeStartDistance) {
|
||||
float t = (distanceToCam - labelFadeStartDistance) / (labelMaxDistance - labelFadeStartDistance);
|
||||
visibleAlpha = Mathf.Clamp01(1f - t);
|
||||
}
|
||||
if (canvasGroup != null) {
|
||||
canvasGroup.alpha = highlightAlpha * visibleAlpha;
|
||||
}
|
||||
|
||||
if (isWorldSpace) {
|
||||
UpdatePositionWorldSpace(worldWithOffset, distanceToCam);
|
||||
} else {
|
||||
UpdatePositionScreenSpace(worldWithOffset, distanceToCam);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePositionWorldSpace(Vector3 worldWithOffset, float distanceToCam) {
|
||||
// Position canvas in world space
|
||||
transform.position = worldWithOffset;
|
||||
|
||||
// Billboard: face camera
|
||||
transform.rotation = cam.transform.rotation;
|
||||
|
||||
// Scale: base worldSpaceScale + optional distance scaling
|
||||
float scale = worldSpaceScale;
|
||||
if (labelScaleByDistance && labelMaxDistance > 0f) {
|
||||
float tScale = Mathf.Clamp01(distanceToCam / labelMaxDistance);
|
||||
scale *= Mathf.Lerp(labelScaleMax, labelScaleMin, tScale);
|
||||
}
|
||||
transform.localScale = new Vector3(scale, scale, scale);
|
||||
|
||||
// Panel keeps identity transform in world space mode
|
||||
panel.localScale = Vector3.one;
|
||||
panel.localPosition = Vector3.zero;
|
||||
}
|
||||
|
||||
void UpdatePositionScreenSpace(Vector3 worldWithOffset, float distanceToCam) {
|
||||
// If anchor point is outside of view, hide label without changing active state
|
||||
Vector3 vp = cam.WorldToViewportPoint(worldWithOffset);
|
||||
bool onScreen = vp.z > 0f && vp.x >= 0f && vp.x <= 1f && vp.y >= 0f && vp.y <= 1f;
|
||||
if (!onScreen) {
|
||||
if (canvasGroup != null) {
|
||||
canvasGroup.alpha = 0f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (labelScaleByDistance && labelMaxDistance > 0f) {
|
||||
float tScale = Mathf.Clamp01(distanceToCam / labelMaxDistance);
|
||||
float scale = Mathf.Lerp(labelScaleMax, labelScaleMin, tScale);
|
||||
panel.localScale = new Vector3(scale, scale, 1f);
|
||||
} else {
|
||||
panel.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
Vector3 screenPosition = cam.WorldToScreenPoint(worldWithOffset);
|
||||
// Apply viewport offset in screen space (normalized -1 to 1 range)
|
||||
screenPosition.x += viewportOffset.x * Screen.width;
|
||||
screenPosition.y += viewportOffset.y * Screen.height;
|
||||
panel.position = screenPosition;
|
||||
|
||||
bool flip = false;
|
||||
if (labelRelativeAlignment && labelAlignmentTransform != null) {
|
||||
Vector3 dirToCam = (cam.transform.position - labelAlignmentTransform.position).normalized;
|
||||
float dot = Vector3.Dot(labelAlignmentTransform.forward, dirToCam);
|
||||
flip = dot > 0;
|
||||
}
|
||||
|
||||
if (labelAlignment == LabelAlignment.Left) {
|
||||
panel.pivot = flip ? Vector2.zero : Vector2.right;
|
||||
text.alignment = flip ? TextAlignmentOptions.BottomRight : TextAlignmentOptions.BottomLeft;
|
||||
} else if (labelAlignment == LabelAlignment.Right) {
|
||||
panel.pivot = flip ? Vector2.right : Vector2.zero;
|
||||
text.alignment = flip ? TextAlignmentOptions.BottomLeft : TextAlignmentOptions.BottomRight;
|
||||
} else if (labelAlignment == LabelAlignment.Auto && panel.position.x + width * 2f > cam.pixelWidth * 0.95f) {
|
||||
panel.pivot = Vector2.right;
|
||||
text.alignment = TextAlignmentOptions.BottomLeft;
|
||||
} else {
|
||||
panel.pivot = Vector2.zero;
|
||||
text.alignment = TextAlignmentOptions.BottomRight;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the label
|
||||
/// </summary>
|
||||
public virtual void Show () {
|
||||
isVisible = true;
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) {
|
||||
HighlightLabelPoolManager.Refresh();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide the label
|
||||
/// </summary>
|
||||
public virtual void Hide () {
|
||||
if (this == null) return;
|
||||
gameObject.SetActive(false);
|
||||
isVisible = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide & destroy the label
|
||||
/// </summary>
|
||||
public virtual void Release () {
|
||||
if (this == null) return;
|
||||
Hide();
|
||||
if (isPooled) {
|
||||
ReturnToPool();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/HighlightLabel.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/HighlightLabel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e63182d0d6a34016b819144ebdd9188
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
[ExecuteAlways]
|
||||
[DefaultExecutionOrder(-100)]
|
||||
public class HighlightLabelPoolManager : MonoBehaviour {
|
||||
|
||||
private static HighlightLabelPoolManager instance;
|
||||
private static readonly Dictionary<GameObject, Queue<HighlightLabel>> labelPools = new Dictionary<GameObject, Queue<HighlightLabel>>();
|
||||
private static readonly HashSet<HighlightLabel> activeLabels = new HashSet<HighlightLabel>();
|
||||
private static readonly int initialPoolSize = 5;
|
||||
|
||||
const string LABEL_INSTANCE_NAME = "Highlight Plus Label";
|
||||
const string LABEL_POOL_NAME = "Highlight Plus Label Pool Manager";
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
static void DomainReloadDisabledSupport () {
|
||||
DestroySceneLabels();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
if (instance == null) {
|
||||
instance = this;
|
||||
} else if (instance != this) {
|
||||
DestroyImmediate(gameObject);
|
||||
}
|
||||
ClearPools();
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (instance == this) {
|
||||
instance = null;
|
||||
}
|
||||
labelPools.Clear();
|
||||
activeLabels.Clear();
|
||||
}
|
||||
|
||||
|
||||
public static void DestroySceneLabels () {
|
||||
if (instance != null) {
|
||||
DestroyImmediate(instance.gameObject);
|
||||
instance = null;
|
||||
}
|
||||
ClearPools();
|
||||
}
|
||||
|
||||
static void ClearPools () {
|
||||
if (instance != null) {
|
||||
for (int i = instance.transform.childCount - 1; i >= 0; i--) {
|
||||
Transform child = instance.transform.GetChild(i);
|
||||
if (child != null) {
|
||||
DestroyImmediate(child.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
labelPools.Clear();
|
||||
activeLabels.Clear();
|
||||
}
|
||||
|
||||
private static void InitializePool (GameObject prefab) {
|
||||
if (!labelPools.ContainsKey(prefab)) {
|
||||
labelPools[prefab] = new Queue<HighlightLabel>();
|
||||
for (int i = 0; i < initialPoolSize; i++) {
|
||||
CreatePooledLabel(prefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HighlightLabel CreatePooledLabel (GameObject prefab) {
|
||||
GameObject labelInstanceGO = Instantiate(prefab);
|
||||
labelInstanceGO.name = LABEL_INSTANCE_NAME;
|
||||
labelInstanceGO.hideFlags = HideFlags.DontSave;
|
||||
labelInstanceGO.transform.SetParent(instance.transform, false);
|
||||
HighlightLabel labelInstance = labelInstanceGO.GetComponentInChildren<HighlightLabel>();
|
||||
labelInstance.labelPrefab = prefab;
|
||||
labelInstance.isPooled = true;
|
||||
labelInstanceGO.SetActive(false);
|
||||
labelPools[prefab].Enqueue(labelInstance);
|
||||
return labelInstance;
|
||||
}
|
||||
|
||||
public static HighlightLabel GetLabelInstance (GameObject prefab) {
|
||||
if (instance == null) {
|
||||
GameObject go = new GameObject(LABEL_POOL_NAME);
|
||||
go.hideFlags = HideFlags.DontSave;
|
||||
instance = go.AddComponent<HighlightLabelPoolManager>();
|
||||
}
|
||||
|
||||
// Initialize pool if needed
|
||||
InitializePool(prefab);
|
||||
|
||||
// Try to get from pool
|
||||
HighlightLabel labelInstance = null;
|
||||
while (labelInstance == null && labelPools[prefab].Count > 0) {
|
||||
labelInstance = labelPools[prefab].Dequeue();
|
||||
}
|
||||
if (labelInstance != null) {
|
||||
activeLabels.Add(labelInstance);
|
||||
return labelInstance;
|
||||
}
|
||||
|
||||
// Create new instance if pool is empty
|
||||
HighlightLabel newLabel = CreatePooledLabel(prefab);
|
||||
activeLabels.Add(newLabel);
|
||||
return newLabel;
|
||||
}
|
||||
|
||||
public static void ReturnToPool (HighlightLabel label) {
|
||||
if (label.labelPrefab != null && labelPools.ContainsKey(label.labelPrefab)) {
|
||||
labelPools[label.labelPrefab].Enqueue(label);
|
||||
activeLabels.Remove(label);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Refresh () {
|
||||
if (instance == null) return;
|
||||
instance.LateUpdate();
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
foreach (var label in activeLabels) {
|
||||
if (label == null) continue;
|
||||
if (label.isVisible) {
|
||||
label.UpdatePosition();
|
||||
if (!label.gameObject.activeSelf) {
|
||||
label.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7d255a67ae894a2c87031baca521c15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
590
Assets/HighlightPlus/Runtime/Scripts/HighlightManager.cs
Normal file
590
Assets/HighlightPlus/Runtime/Scripts/HighlightManager.cs
Normal file
@@ -0,0 +1,590 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
[RequireComponent(typeof(HighlightEffect))]
|
||||
[DefaultExecutionOrder(100)]
|
||||
[HelpURL("https://kronnect.com/docs/highlight-plus/")]
|
||||
public class HighlightManager : MonoBehaviour {
|
||||
|
||||
[Tooltip("Enables highlight when pointer is over this object.")]
|
||||
[SerializeField]
|
||||
bool _highlightOnHover = true;
|
||||
|
||||
public bool highlightOnHover {
|
||||
get { return _highlightOnHover; }
|
||||
set {
|
||||
if (_highlightOnHover != value) {
|
||||
_highlightOnHover = value;
|
||||
if (!_highlightOnHover) {
|
||||
if (currentEffect != null) {
|
||||
Highlight(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LayerMask layerMask = -1;
|
||||
public Camera raycastCamera;
|
||||
public RayCastSource raycastSource = RayCastSource.MousePosition;
|
||||
[Tooltip("Objects in this layer mask will block the highlight if they are between the camera and the target.")]
|
||||
public LayerMask blockingLayerMask = -1;
|
||||
[Tooltip("Minimum distance for target.")]
|
||||
public float minDistance;
|
||||
[Tooltip("Maximum distance for target. 0 = infinity")]
|
||||
public float maxDistance;
|
||||
[Tooltip("Blocks interaction if pointer is over an UI element")]
|
||||
public bool respectUI = true;
|
||||
[Tooltip("Unhighlights the object when the pointer is over a UI element")]
|
||||
public bool unhighlightOnUI;
|
||||
|
||||
[Tooltip("If the object will be selected by clicking with mouse or tapping on it.")]
|
||||
public bool selectOnClick;
|
||||
[Tooltip("Optional profile for objects selected by clicking on them")]
|
||||
public HighlightProfile selectedProfile;
|
||||
[Tooltip("Profile to use when object is selected and highlighted.")]
|
||||
public HighlightProfile selectedAndHighlightedProfile;
|
||||
[Tooltip("Automatically deselects other previously selected objects")]
|
||||
public bool singleSelection;
|
||||
[Tooltip("Toggles selection on/off when clicking object")]
|
||||
public bool toggle;
|
||||
[Tooltip("Keeps current selection when clicking outside of any selectable object")]
|
||||
public bool keepSelection = true;
|
||||
|
||||
HighlightEffect baseEffect, currentEffect;
|
||||
Transform currentObject;
|
||||
RaycastHit2D[] hitInfo2D;
|
||||
static RaycastHit blockingHit;
|
||||
readonly List<RaycastResult> cachedRaycastResults = new List<RaycastResult>();
|
||||
PointerEventData cachedPointerEventData;
|
||||
EventSystem cachedEventSystem;
|
||||
|
||||
public readonly static List<HighlightEffect> selectedObjects = new List<HighlightEffect>();
|
||||
public event OnObjectSelectionEvent OnObjectSelected;
|
||||
public event OnObjectSelectionEvent OnObjectUnSelected;
|
||||
public event OnObjectHighlightEvent OnObjectHighlightStart;
|
||||
public event OnObjectHighlightEvent OnObjectHighlightStay;
|
||||
public event OnObjectHighlightEvent OnObjectHighlightEnd;
|
||||
public event OnObjectClickEvent OnObjectClicked;
|
||||
public static int lastTriggerFrame;
|
||||
|
||||
static HighlightManager _instance;
|
||||
public static HighlightManager instance {
|
||||
get {
|
||||
if (_instance == null) {
|
||||
_instance = Misc.FindObjectOfType<HighlightManager>();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
static void DomainReloadDisabledSupport () {
|
||||
selectedObjects.Clear();
|
||||
lastTriggerFrame = 0;
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
currentObject = null;
|
||||
currentEffect = null;
|
||||
if (baseEffect == null) {
|
||||
if (!TryGetComponent(out baseEffect)) {
|
||||
baseEffect = gameObject.AddComponent<HighlightEffect>();
|
||||
}
|
||||
}
|
||||
if (raycastCamera == null) {
|
||||
raycastCamera = GetCamera();
|
||||
if (raycastCamera == null) {
|
||||
Debug.LogError("Highlight Manager: no camera found!");
|
||||
}
|
||||
}
|
||||
hitInfo2D = new RaycastHit2D[1];
|
||||
InputProxy.Init();
|
||||
}
|
||||
|
||||
|
||||
void OnDisable () {
|
||||
SwitchesObject(null);
|
||||
internal_DeselectAll();
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (raycastCamera == null)
|
||||
return;
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if (respectUI) {
|
||||
EventSystem es = EventSystem.current;
|
||||
if (es == null) {
|
||||
es = CreateEventSystem();
|
||||
}
|
||||
cachedRaycastResults.Clear();
|
||||
if (cachedPointerEventData == null || cachedEventSystem != es) {
|
||||
cachedPointerEventData = new PointerEventData(es);
|
||||
cachedEventSystem = es;
|
||||
}
|
||||
Vector3 cameraPos = raycastCamera.transform.position;
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
cachedPointerEventData.position = InputProxy.mousePosition;
|
||||
} else {
|
||||
cachedPointerEventData.position = new Vector2(raycastCamera.pixelWidth * 0.5f, raycastCamera.pixelHeight * 0.5f);
|
||||
}
|
||||
es.RaycastAll(cachedPointerEventData, cachedRaycastResults);
|
||||
int hitCount = cachedRaycastResults.Count;
|
||||
// check UI blocker
|
||||
bool blocked = false;
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
RaycastResult rr = cachedRaycastResults[k];
|
||||
if (rr.module is UnityEngine.UI.GraphicRaycaster) {
|
||||
blocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blocked) {
|
||||
if (unhighlightOnUI && currentEffect != null && currentEffect.highlighted) {
|
||||
currentEffect.SetHighlighted(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// compute blocking ray for distance checks
|
||||
Ray blockingRay;
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
blockingRay = raycastCamera.ScreenPointToRay(InputProxy.mousePosition);
|
||||
} else {
|
||||
blockingRay = new Ray(raycastCamera.transform.position, raycastCamera.transform.forward);
|
||||
}
|
||||
float uiBlockingDistance = GetBlockingDistance(blockingRay, maxDistance > 0 ? maxDistance : raycastCamera.farClipPlane);
|
||||
|
||||
// look for our gameobject
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
RaycastResult rr = cachedRaycastResults[k];
|
||||
float distance = Vector3.Distance(rr.worldPosition, cameraPos);
|
||||
if (distance < minDistance || (maxDistance > 0 && distance > maxDistance) || distance > uiBlockingDistance) continue;
|
||||
|
||||
GameObject theGameObject = rr.gameObject;
|
||||
if ((layerMask & (1 << rr.gameObject.layer)) == 0) continue;
|
||||
|
||||
// is this object state controller by Highlight Trigger?
|
||||
HighlightTrigger trigger = theGameObject.GetComponent<HighlightTrigger>();
|
||||
if (trigger != null) return;
|
||||
|
||||
UpdateHitPosition(theGameObject.transform, rr.worldPosition, Misc.vector3Zero);
|
||||
|
||||
// Toggles selection
|
||||
Transform t = theGameObject.transform;
|
||||
if (InputProxy.GetMouseButtonDown(0)) {
|
||||
if (selectOnClick) {
|
||||
ToggleSelection(t, !toggle);
|
||||
}
|
||||
} else
|
||||
// Check if the object has a Highlight Effect
|
||||
if (t != currentObject) {
|
||||
SwitchesObject(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if not blocked by UI and no hit found, fallback to raycast (required if no PhysicsRaycaster is present on the camera)
|
||||
#endif
|
||||
|
||||
Ray ray;
|
||||
{
|
||||
#if !(ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER)
|
||||
Vector2 screenPos = raycastSource == RayCastSource.MousePosition ? (Vector2)InputProxy.mousePosition : new Vector2(raycastCamera.pixelWidth * 0.5f, raycastCamera.pixelHeight * 0.5f);
|
||||
if (!CanInteractAt(screenPos)) {
|
||||
if (unhighlightOnUI && currentEffect != null && currentEffect.highlighted) {
|
||||
currentEffect.SetHighlighted(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
ray = raycastCamera.ScreenPointToRay(InputProxy.mousePosition);
|
||||
}
|
||||
else {
|
||||
ray = new Ray(raycastCamera.transform.position, raycastCamera.transform.forward);
|
||||
}
|
||||
}
|
||||
|
||||
VerifyHighlightStay();
|
||||
|
||||
float blockingDistance = GetBlockingDistance(ray, maxDistance > 0 ? maxDistance : raycastCamera.farClipPlane);
|
||||
RaycastHit hitInfo;
|
||||
if (Physics.Raycast(ray, out hitInfo, maxDistance > 0 ? maxDistance : raycastCamera.farClipPlane, layerMask) && Vector3.Distance(hitInfo.point, ray.origin) >= minDistance && hitInfo.distance <= blockingDistance) {
|
||||
Transform t = hitInfo.collider.transform;
|
||||
// is this object state controller by Highlight Trigger?
|
||||
if (t.TryGetComponent(out HighlightTrigger _)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggles selection
|
||||
if (InputProxy.GetMouseButtonDown(0)) {
|
||||
if (selectOnClick) {
|
||||
ToggleSelection(t, !toggle);
|
||||
}
|
||||
}
|
||||
else
|
||||
// Check if the object has a Highlight Effect
|
||||
if (t != currentObject) {
|
||||
SwitchesObject(t);
|
||||
}
|
||||
UpdateHitPosition(t, hitInfo.point, hitInfo.normal);
|
||||
return;
|
||||
}
|
||||
else // check sprites
|
||||
if (Physics2D.GetRayIntersectionNonAlloc(ray, hitInfo2D, maxDistance > 0 ? maxDistance : raycastCamera.farClipPlane, layerMask) > 0 && Vector3.Distance(hitInfo2D[0].point, ray.origin) >= minDistance && Vector3.Distance(hitInfo2D[0].point, ray.origin) <= blockingDistance) {
|
||||
Transform t = hitInfo2D[0].collider.transform;
|
||||
// is this object state controller by Highlight Trigger?
|
||||
if (t.TryGetComponent(out HighlightTrigger _)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggles selection
|
||||
if (InputProxy.GetMouseButtonDown(0)) {
|
||||
if (selectOnClick) {
|
||||
ToggleSelection(t, !toggle);
|
||||
}
|
||||
}
|
||||
else
|
||||
// Check if the object has a Highlight Effect
|
||||
if (t != currentObject) {
|
||||
SwitchesObject(t);
|
||||
}
|
||||
UpdateHitPosition(t, hitInfo2D[0].point, Misc.vector3Zero);
|
||||
return;
|
||||
}
|
||||
|
||||
// no hit
|
||||
if (selectOnClick && !keepSelection && InputProxy.GetMouseButtonDown(0) && lastTriggerFrame < Time.frameCount) {
|
||||
internal_DeselectAll();
|
||||
}
|
||||
SwitchesObject(null);
|
||||
}
|
||||
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
EventSystem CreateEventSystem() {
|
||||
GameObject eo = new GameObject("Event System created by Highlight Plus", typeof(EventSystem), typeof(UnityEngine.InputSystem.UI.InputSystemUIInputModule));
|
||||
return eo.GetComponent<EventSystem>();
|
||||
}
|
||||
#endif
|
||||
void VerifyHighlightStay () {
|
||||
if (currentObject == null || currentEffect == null || !currentEffect.highlighted) return;
|
||||
if (OnObjectHighlightStay != null && !OnObjectHighlightStay(currentObject.gameObject)) {
|
||||
SwitchesObject(null);
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchesObject (Transform newObject) {
|
||||
if (currentEffect != null) {
|
||||
if (highlightOnHover) {
|
||||
Highlight(false);
|
||||
}
|
||||
currentEffect = null;
|
||||
}
|
||||
currentObject = newObject;
|
||||
if (newObject == null) return;
|
||||
if (newObject.TryGetComponent(out HighlightTrigger ht) && ht.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newObject.TryGetComponent(out HighlightEffect otherEffect)) {
|
||||
// Check if there's a parent highlight effect that includes this object
|
||||
HighlightEffect parentEffect = newObject.GetComponentInParent<HighlightEffect>();
|
||||
if (parentEffect != null && parentEffect.Includes(newObject)) {
|
||||
currentEffect = parentEffect;
|
||||
if (highlightOnHover) {
|
||||
Highlight(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentEffect = otherEffect != null ? otherEffect : baseEffect;
|
||||
baseEffect.enabled = currentEffect == baseEffect;
|
||||
currentEffect.SetTarget(currentObject);
|
||||
|
||||
if (highlightOnHover) {
|
||||
Highlight(true);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateHitPosition (Transform target, Vector3 positionWS, Vector3 normalWS) {
|
||||
if (currentEffect == null) return;
|
||||
Vector3 localPosition = target.InverseTransformPoint(positionWS);
|
||||
currentEffect.SetHitPosition(target, localPosition, Misc.vector3Zero, normalWS);
|
||||
|
||||
if (InputProxy.GetMouseButtonDown(0) && OnObjectClicked != null) {
|
||||
OnObjectClicked(target.gameObject, positionWS, normalWS);
|
||||
}
|
||||
}
|
||||
|
||||
#if !(ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER)
|
||||
bool CanInteractAt (Vector2 screenPosition) {
|
||||
if (!respectUI) return true;
|
||||
EventSystem es = EventSystem.current;
|
||||
if (es == null) return true;
|
||||
if (Application.isMobilePlatform && InputProxy.touchCount > 0) {
|
||||
if (es.IsPointerOverGameObject(InputProxy.GetFingerIdFromTouch(0))) {
|
||||
return false;
|
||||
}
|
||||
if (raycastSource == RayCastSource.CameraDirection) {
|
||||
return !IsUIAtScreenPosition(es, screenPosition);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
return !es.IsPointerOverGameObject(-1);
|
||||
}
|
||||
return !IsUIAtScreenPosition(es, screenPosition);
|
||||
}
|
||||
|
||||
bool IsUIAtScreenPosition (EventSystem es, Vector2 screenPosition) {
|
||||
cachedRaycastResults.Clear();
|
||||
if (cachedPointerEventData == null || cachedEventSystem != es) {
|
||||
cachedPointerEventData = new PointerEventData(es);
|
||||
cachedEventSystem = es;
|
||||
}
|
||||
cachedPointerEventData.position = screenPosition;
|
||||
es.RaycastAll(cachedPointerEventData, cachedRaycastResults);
|
||||
int hitCount = cachedRaycastResults.Count;
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
RaycastResult rr = cachedRaycastResults[k];
|
||||
if (rr.module is UnityEngine.UI.GraphicRaycaster) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
float GetBlockingDistance (Ray ray, float maxRayDistance) {
|
||||
if (blockingLayerMask == 0) return float.MaxValue;
|
||||
if (Physics.Raycast(ray, out blockingHit, maxRayDistance, blockingLayerMask)) {
|
||||
return blockingHit.distance + 0.01f;
|
||||
}
|
||||
return float.MaxValue;
|
||||
}
|
||||
|
||||
void ToggleSelection (Transform t, bool forceSelection) {
|
||||
|
||||
if (t == null) return;
|
||||
// We need a highlight effect on each selected object
|
||||
HighlightEffect hb = t.GetComponent<HighlightEffect>();
|
||||
if (hb == null) {
|
||||
HighlightEffect parentEffect = t.GetComponentInParent<HighlightEffect>();
|
||||
if (parentEffect != null && parentEffect.Includes(t)) {
|
||||
hb = parentEffect;
|
||||
if (hb.previousSettings == null) {
|
||||
hb.previousSettings = ScriptableObject.CreateInstance<HighlightProfile>();
|
||||
}
|
||||
hb.previousSettings.Save(hb);
|
||||
}
|
||||
else {
|
||||
hb = t.gameObject.AddComponent<HighlightEffect>();
|
||||
hb.camerasLayerMask = baseEffect.camerasLayerMask;
|
||||
hb.ignoreObjectVisibility = baseEffect.ignoreObjectVisibility;
|
||||
hb.reflectionProbes = baseEffect.reflectionProbes;
|
||||
hb.normalsOption = baseEffect.normalsOption;
|
||||
hb.optimizeSkinnedMesh = baseEffect.optimizeSkinnedMesh;
|
||||
hb.GPUInstancing = baseEffect.GPUInstancing;
|
||||
|
||||
hb.previousSettings = ScriptableObject.CreateInstance<HighlightProfile>();
|
||||
// copy default highlight effect settings from this manager into this highlight plus component
|
||||
hb.previousSettings.Save(baseEffect);
|
||||
hb.previousSettings.Load(hb);
|
||||
}
|
||||
}
|
||||
|
||||
bool currentState = hb.isSelected;
|
||||
bool newState = forceSelection ? true : !currentState;
|
||||
if (newState == currentState) return;
|
||||
|
||||
if (newState) {
|
||||
if (OnObjectSelected != null && !OnObjectSelected(t.gameObject)) return;
|
||||
}
|
||||
else {
|
||||
if (OnObjectUnSelected != null && !OnObjectUnSelected(t.gameObject)) return;
|
||||
}
|
||||
|
||||
if (singleSelection) {
|
||||
internal_DeselectAll();
|
||||
}
|
||||
|
||||
currentEffect = hb;
|
||||
currentEffect.isSelected = newState;
|
||||
baseEffect.enabled = false;
|
||||
|
||||
if (currentEffect.isSelected) {
|
||||
if (currentEffect.previousSettings == null) {
|
||||
currentEffect.previousSettings = ScriptableObject.CreateInstance<HighlightProfile>();
|
||||
}
|
||||
hb.previousSettings.Save(hb);
|
||||
|
||||
if (!selectedObjects.Contains(currentEffect)) {
|
||||
selectedObjects.Add(currentEffect);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (currentEffect.previousSettings != null) {
|
||||
currentEffect.previousSettings.Load(hb);
|
||||
}
|
||||
if (selectedObjects.Contains(currentEffect)) {
|
||||
selectedObjects.Remove(currentEffect);
|
||||
}
|
||||
}
|
||||
|
||||
Highlight(newState);
|
||||
}
|
||||
|
||||
void Highlight (bool state) {
|
||||
if (currentEffect == null) return;
|
||||
|
||||
if (state) {
|
||||
if (!currentEffect.highlighted) {
|
||||
if (OnObjectHighlightStart != null && currentEffect.target != null) {
|
||||
if (!OnObjectHighlightStart(currentEffect.target.gameObject)) {
|
||||
currentObject = null; // allows re-checking so it keeps checking with the event
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (currentEffect.highlighted) {
|
||||
if (OnObjectHighlightEnd != null && currentEffect.target != null) {
|
||||
OnObjectHighlightEnd(currentEffect.target.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectOnClick || currentEffect.isSelected) {
|
||||
if (currentEffect.isSelected) {
|
||||
if (state && selectedAndHighlightedProfile != null) {
|
||||
selectedAndHighlightedProfile.Load(currentEffect);
|
||||
}
|
||||
else if (selectedProfile != null) {
|
||||
selectedProfile.Load(currentEffect);
|
||||
}
|
||||
else {
|
||||
currentEffect.previousSettings.Load(currentEffect);
|
||||
}
|
||||
if (currentEffect.highlighted && currentEffect.fading != HighlightEffect.FadingState.FadingOut) {
|
||||
currentEffect.UpdateMaterialProperties();
|
||||
}
|
||||
else {
|
||||
currentEffect.SetHighlighted(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (!highlightOnHover) {
|
||||
currentEffect.SetHighlighted(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentEffect.SetHighlighted(state);
|
||||
}
|
||||
|
||||
public static Camera GetCamera () {
|
||||
Camera raycastCamera = Camera.main;
|
||||
if (raycastCamera == null) {
|
||||
raycastCamera = Misc.FindObjectOfType<Camera>();
|
||||
}
|
||||
return raycastCamera;
|
||||
}
|
||||
|
||||
void internal_DeselectAll () {
|
||||
int count = selectedObjects.Count;
|
||||
for (int k = 0; k < count; k++) {
|
||||
HighlightEffect hb = selectedObjects[k];
|
||||
if (hb != null && hb.gameObject != null) {
|
||||
if (OnObjectUnSelected != null) {
|
||||
if (!OnObjectUnSelected(hb.gameObject)) continue;
|
||||
}
|
||||
hb.RestorePreviousHighlightEffectSettings();
|
||||
hb.isSelected = false;
|
||||
hb.SetHighlighted(false);
|
||||
}
|
||||
}
|
||||
selectedObjects.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselects any selected object in the scene
|
||||
/// </summary>
|
||||
public static void DeselectAll () {
|
||||
if (instance != null) {
|
||||
_instance.internal_DeselectAll();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (HighlightEffect hb in selectedObjects) {
|
||||
if (hb != null && hb.gameObject != null) {
|
||||
hb.RestorePreviousHighlightEffectSettings();
|
||||
hb.isSelected = false;
|
||||
hb.SetHighlighted(false);
|
||||
}
|
||||
}
|
||||
selectedObjects.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unselects all objects in the scene
|
||||
/// </summary>
|
||||
public void UnselectObjects () {
|
||||
DeselectAll();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Manually causes highlight manager to select an object
|
||||
/// </summary>
|
||||
public void SelectObject (Transform t) {
|
||||
ToggleSelection(t, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually causes highlight manager to select multiple objects
|
||||
/// </summary>
|
||||
/// <param name="objects">Array of objects to select</param>
|
||||
public void SelectObjects (Transform[] objects) {
|
||||
foreach (var obj in objects) {
|
||||
SelectObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectObjects (List<Transform> objects) {
|
||||
foreach (var obj in objects) {
|
||||
SelectObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually causes highlight manager to toggle selection on an object
|
||||
/// </summary>
|
||||
public void ToggleObject (Transform t) {
|
||||
ToggleSelection(t, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually causes highlight manager to unselect an object
|
||||
/// </summary>
|
||||
public void UnselectObject (Transform t) {
|
||||
if (t == null) return;
|
||||
HighlightEffect hb = t.GetComponent<HighlightEffect>();
|
||||
if (hb == null) return;
|
||||
|
||||
if (hb.isSelected) {
|
||||
ToggleSelection(t, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af4d46fd89b9543e5be2358ac0c9ced0
|
||||
timeCreated: 1542876337
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
#if UNITY_2023_3_OR_NEWER
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
#endif
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public class HighlightPlusRenderPassFeature : ScriptableRendererFeature {
|
||||
|
||||
class HighlightPass : ScriptableRenderPass {
|
||||
|
||||
class PassData {
|
||||
public Camera camera;
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
public RTHandle colorTarget, depthTarget;
|
||||
#else
|
||||
public RenderTargetIdentifier colorTarget, depthTarget;
|
||||
#endif
|
||||
#if UNITY_2023_3_OR_NEWER
|
||||
public TextureHandle colorTexture, depthTexture;
|
||||
#endif
|
||||
public bool clearStencil;
|
||||
public CommandBuffer cmd;
|
||||
}
|
||||
|
||||
readonly PassData passData = new PassData();
|
||||
|
||||
// far objects render first
|
||||
class DistanceComparer : IComparer<HighlightEffect> {
|
||||
|
||||
public Vector3 camPos;
|
||||
|
||||
public int Compare (HighlightEffect e1, HighlightEffect e2) {
|
||||
if (e1.sortingPriority < e2.sortingPriority) return -1;
|
||||
if (e1.sortingPriority > e2.sortingPriority) return 1;
|
||||
Vector3 e1Pos = e1.transform.position;
|
||||
float dx1 = e1Pos.x - camPos.x;
|
||||
float dy1 = e1Pos.y - camPos.y;
|
||||
float dz1 = e1Pos.z - camPos.z;
|
||||
float distE1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1 + e1.sortingOffset;
|
||||
Vector3 e2Pos = e2.transform.position;
|
||||
float dx2 = e2Pos.x - camPos.x;
|
||||
float dy2 = e2Pos.y - camPos.y;
|
||||
float dz2 = e2Pos.z - camPos.z;
|
||||
float distE2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2 + e2.sortingOffset;
|
||||
if (distE1 > distE2) return -1;
|
||||
if (distE1 < distE2) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool usesCameraOverlay;
|
||||
|
||||
ScriptableRenderer renderer;
|
||||
RenderTextureDescriptor cameraTextureDescriptor;
|
||||
static DistanceComparer effectDistanceComparer = new DistanceComparer();
|
||||
static Comparison<HighlightEffect> cachedEffectComparisonDelegate;
|
||||
bool clearStencil;
|
||||
static RenderTextureDescriptor sourceDesc;
|
||||
static Material blockerOutlineAndGlowMat, blockerOverlayMat, blockerAllMat;
|
||||
|
||||
public void Setup (HighlightPlusRenderPassFeature passFeature, ScriptableRenderer renderer) {
|
||||
this.renderPassEvent = passFeature.renderPassEvent;
|
||||
this.clearStencil = passFeature.clearStencil;
|
||||
this.renderer = renderer;
|
||||
ConfigureInput(ScriptableRenderPassInput.Depth);
|
||||
}
|
||||
|
||||
#if !UNITY_6000_4_OR_NEWER
|
||||
#if UNITY_2023_3_OR_NEWER
|
||||
[Obsolete]
|
||||
#endif
|
||||
public override void Configure (CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) {
|
||||
this.cameraTextureDescriptor = cameraTextureDescriptor;
|
||||
}
|
||||
|
||||
#if UNITY_2023_3_OR_NEWER
|
||||
[Obsolete]
|
||||
#endif
|
||||
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
RTHandle cameraColorTarget = renderer.cameraColorTargetHandle;
|
||||
RTHandle cameraDepthTarget = renderer.cameraDepthTargetHandle;
|
||||
#else
|
||||
RenderTargetIdentifier cameraColorTarget = renderer.cameraColorTarget;
|
||||
RenderTargetIdentifier cameraDepthTarget = renderer.cameraDepthTarget;
|
||||
#endif
|
||||
#if !UNITY_2021_2_OR_NEWER
|
||||
// In Unity 2021.2, when MSAA > 1, cameraDepthTarget is no longer cameraColorTarget
|
||||
if (!usesCameraOverlay && (cameraTextureDescriptor.msaaSamples > 1 || cam.cameraType == CameraType.SceneView)) {
|
||||
cameraDepthTarget = cameraColorTarget;
|
||||
}
|
||||
#endif
|
||||
|
||||
passData.clearStencil = clearStencil;
|
||||
passData.camera = renderingData.cameraData.camera;
|
||||
passData.colorTarget = cameraColorTarget;
|
||||
passData.depthTarget = cameraDepthTarget;
|
||||
sourceDesc = renderingData.cameraData.cameraTargetDescriptor;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get("Highlight Plus");
|
||||
cmd.Clear();
|
||||
|
||||
passData.cmd = cmd;
|
||||
ExecutePass(passData);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ExecutePass (PassData passData) {
|
||||
|
||||
int count = HighlightEffect.effects.Count;
|
||||
|
||||
HighlightEffect.effects.RemoveAll(t => t == null);
|
||||
count = HighlightEffect.effects.Count;
|
||||
if (count == 0) return;
|
||||
|
||||
Camera cam = passData.camera;
|
||||
int camLayer = 1 << cam.gameObject.layer;
|
||||
CameraType camType = cam.cameraType;
|
||||
|
||||
if (!HighlightEffect.customSorting && ((camType == CameraType.Game && (sortFrameCount++) % 10 == 0) || !Application.isPlaying)) {
|
||||
effectDistanceComparer.camPos = cam.transform.position;
|
||||
if (cachedEffectComparisonDelegate == null) {
|
||||
cachedEffectComparisonDelegate = effectDistanceComparer.Compare;
|
||||
}
|
||||
HighlightEffect.effects.Sort(cachedEffectComparisonDelegate);
|
||||
}
|
||||
|
||||
bool outlineOccludersPending = outlineAndGlowOccluders.Count > 0;
|
||||
HighlightEffect.focusRendered = false;
|
||||
|
||||
for (int k = 0; k < count; k++) {
|
||||
HighlightEffect effect = HighlightEffect.effects[k];
|
||||
|
||||
if (!(effect.ignoreObjectVisibility || effect.isVisible)) continue;
|
||||
|
||||
if (!effect.isActiveAndEnabled) continue;
|
||||
|
||||
if (camType == CameraType.Reflection && !effect.reflectionProbes) continue;
|
||||
|
||||
if ((effect.camerasLayerMask & camLayer) == 0) continue;
|
||||
|
||||
if (outlineOccludersPending) {
|
||||
outlineOccludersPending = false;
|
||||
foreach (HighlightEffectBlocker blocker in outlineAndGlowOccluders) {
|
||||
if (blocker != null && blocker.isActiveAndEnabled) {
|
||||
int stencilOp = 0;
|
||||
if (blocker.blockOutlineAndGlow) stencilOp += 2;
|
||||
if (blocker.blockOverlay) stencilOp += 4;
|
||||
if (stencilOp == 2) {
|
||||
if (blockerOutlineAndGlowMat == null) {
|
||||
blockerOutlineAndGlowMat = Resources.Load<Material>("HighlightPlus/HighlightBlockerOutlineAndGlow");
|
||||
}
|
||||
blocker.BuildCommandBuffer(passData.cmd, blockerOutlineAndGlowMat);
|
||||
}
|
||||
else if (stencilOp == 4) {
|
||||
if (blockerOverlayMat == null) {
|
||||
blockerOverlayMat = Resources.Load<Material>("HighlightPlus/HighlightBlockerOverlay");
|
||||
}
|
||||
blocker.BuildCommandBuffer(passData.cmd, blockerOverlayMat);
|
||||
}
|
||||
else if (stencilOp == 6) {
|
||||
if (blockerAllMat == null) {
|
||||
blockerAllMat = Resources.Load<Material>("HighlightPlus/HighlightUIMask");
|
||||
}
|
||||
blocker.BuildCommandBuffer(passData.cmd, blockerAllMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
effect.SetCommandBuffer(passData.cmd);
|
||||
effect.BuildCommandBuffer(passData.camera, passData.colorTarget, passData.depthTarget, passData.clearStencil, ref sourceDesc);
|
||||
passData.clearStencil = false;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2023_3_OR_NEWER
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) {
|
||||
|
||||
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
||||
if (IsDepthOnlyTarget(cameraData.cameraTargetDescriptor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
|
||||
if (!resourceData.activeColorTexture.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using (var builder = renderGraph.AddUnsafePass<PassData>("Highlight Plus Pass RG", out var passData)) {
|
||||
|
||||
builder.AllowPassCulling(false);
|
||||
|
||||
passData.clearStencil = clearStencil;
|
||||
passData.camera = cameraData.camera;
|
||||
|
||||
passData.colorTexture = resourceData.activeColorTexture;
|
||||
passData.depthTexture = resourceData.activeDepthTexture;
|
||||
|
||||
builder.UseTexture(resourceData.activeColorTexture, AccessFlags.ReadWrite);
|
||||
builder.UseTexture(resourceData.activeDepthTexture, AccessFlags.Read);
|
||||
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
|
||||
|
||||
sourceDesc = cameraData.cameraTargetDescriptor;
|
||||
|
||||
builder.SetRenderFunc((PassData passData, UnsafeGraphContext context) => {
|
||||
CommandBuffer cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
passData.cmd = cmd;
|
||||
passData.colorTarget = passData.colorTexture;
|
||||
passData.depthTarget = passData.depthTexture;
|
||||
ExecutePass(passData);
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
HighlightPass renderPass;
|
||||
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
|
||||
[Tooltip("Clears stencil buffer before rendering highlight effects. This option can solve compatibility issues with shaders that also use stencil buffers.")]
|
||||
public bool clearStencil;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the effects visible in Edit mode.
|
||||
/// </summary>
|
||||
[Tooltip("If enabled, effects will be visible also in Edit mode (when not in Play mode).")]
|
||||
public bool previewInEditMode = true;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the effects visible in Scene View.
|
||||
/// </summary>
|
||||
[Tooltip("If enabled, effects will be visible also in Scene View.")]
|
||||
public bool showInSceneView = true;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the effects visible in Edit mode.
|
||||
/// </summary>
|
||||
[Tooltip("If enabled, effects will be visible also in Preview camera (preview camera shown when a camera is selected in Editor).")]
|
||||
public bool showInPreviewCamera;
|
||||
|
||||
public static bool installed;
|
||||
public static bool showingInEditMode;
|
||||
|
||||
public static List<HighlightEffectBlocker> outlineAndGlowOccluders = new List<HighlightEffectBlocker>();
|
||||
public static int sortFrameCount;
|
||||
|
||||
const string PREVIEW_CAMERA_NAME = "Preview";
|
||||
|
||||
void OnDisable () {
|
||||
installed = false;
|
||||
}
|
||||
|
||||
public override void Create () {
|
||||
renderPass = new HighlightPass();
|
||||
VRCheck.Init();
|
||||
}
|
||||
|
||||
// This method is called when setting up the renderer once per-camera.
|
||||
public override void AddRenderPasses (ScriptableRenderer renderer, ref RenderingData renderingData) {
|
||||
|
||||
if (HighlightEffect.effects.Count == 0) return;
|
||||
|
||||
showingInEditMode = previewInEditMode;
|
||||
Camera cam = renderingData.cameraData.camera;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!previewInEditMode && !Application.isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
CameraType camType = cam.cameraType;
|
||||
|
||||
if (camType == CameraType.SceneView && !showInSceneView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!showInPreviewCamera && (camType == CameraType.Preview || cam.name.StartsWith(PREVIEW_CAMERA_NAME))) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsDepthOnlyTarget(renderingData.cameraData.cameraTargetDescriptor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_2019_4_OR_NEWER
|
||||
if (renderingData.cameraData.renderType == CameraRenderType.Base) {
|
||||
renderPass.usesCameraOverlay = cam.GetUniversalAdditionalCameraData().cameraStack.Count > 0;
|
||||
}
|
||||
#endif
|
||||
renderPass.Setup(this, renderer);
|
||||
renderer.EnqueuePass(renderPass);
|
||||
installed = true;
|
||||
}
|
||||
|
||||
static Material sBlockerOutlineAndGlowMat, sBlockerOverlayMat, sBlockerAllMat;
|
||||
|
||||
public static void RenderBlockers(CommandBuffer cmd) {
|
||||
int count = outlineAndGlowOccluders.Count;
|
||||
if (count == 0) return;
|
||||
for (int i = 0; i < count; i++) {
|
||||
HighlightEffectBlocker blocker = outlineAndGlowOccluders[i];
|
||||
if (blocker == null || !blocker.isActiveAndEnabled) continue;
|
||||
int stencilOp = 0;
|
||||
if (blocker.blockOutlineAndGlow) stencilOp += 2;
|
||||
if (blocker.blockOverlay) stencilOp += 4;
|
||||
if (stencilOp == 2) {
|
||||
if (sBlockerOutlineAndGlowMat == null) {
|
||||
sBlockerOutlineAndGlowMat = Resources.Load<Material>("HighlightPlus/HighlightBlockerOutlineAndGlow");
|
||||
}
|
||||
blocker.BuildCommandBuffer(cmd, sBlockerOutlineAndGlowMat);
|
||||
} else if (stencilOp == 4) {
|
||||
if (sBlockerOverlayMat == null) {
|
||||
sBlockerOverlayMat = Resources.Load<Material>("HighlightPlus/HighlightBlockerOverlay");
|
||||
}
|
||||
blocker.BuildCommandBuffer(cmd, sBlockerOverlayMat);
|
||||
} else if (stencilOp == 6) {
|
||||
if (sBlockerAllMat == null) {
|
||||
sBlockerAllMat = Resources.Load<Material>("HighlightPlus/HighlightUIMask");
|
||||
}
|
||||
blocker.BuildCommandBuffer(cmd, sBlockerAllMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterBlocker (HighlightEffectBlocker occluder) {
|
||||
if (!outlineAndGlowOccluders.Contains(occluder)) {
|
||||
outlineAndGlowOccluders.Add(occluder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterBlocker (HighlightEffectBlocker occluder) {
|
||||
outlineAndGlowOccluders.Remove(occluder);
|
||||
}
|
||||
|
||||
static bool IsDepthOnlyTarget(in RenderTextureDescriptor descriptor) {
|
||||
if (descriptor.colorFormat == RenderTextureFormat.Depth) {
|
||||
return true;
|
||||
}
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (descriptor.graphicsFormat == GraphicsFormat.None) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 473a86c9e274347dfbdde619584cebe9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
829
Assets/HighlightPlus/Runtime/Scripts/HighlightProfile.cs
Normal file
829
Assets/HighlightPlus/Runtime/Scripts/HighlightProfile.cs
Normal file
@@ -0,0 +1,829 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
[CreateAssetMenu(menuName = "Highlight Plus Profile", fileName = "Highlight Plus Profile", order = 100)]
|
||||
[HelpURL("https://kronnect.com/docs/highlight-plus/")]
|
||||
public class HighlightProfile : ScriptableObject {
|
||||
|
||||
[Tooltip("Different options to specify which objects are affected by this Highlight Effect component.")]
|
||||
public TargetOptions effectGroup = TargetOptions.Children;
|
||||
|
||||
[Tooltip("The layer that contains the affected objects by this effect when effectGroup is set to LayerMask.")]
|
||||
public LayerMask effectGroupLayer = -1;
|
||||
|
||||
[Tooltip("Only include objects whose names contains this text.")]
|
||||
public string effectNameFilter;
|
||||
|
||||
[Tooltip("Use RegEx to determine if an object name matches the effectNameFilter.")]
|
||||
public bool effectNameUseRegEx;
|
||||
|
||||
[Tooltip("Exclude objects that already have a Highlight Effect component when using Include options.")]
|
||||
public bool excludeObjectsWithHighlightEffect;
|
||||
|
||||
[Tooltip("Combine meshes of all objects in this group affected by Highlight Effect reducing draw calls.")]
|
||||
public bool combineMeshes;
|
||||
|
||||
[Tooltip("The alpha threshold for transparent cutout objects. Pixels with alpha below this value will be discarded.")]
|
||||
[Range(0, 1)]
|
||||
public float alphaCutOff;
|
||||
[Tooltip("Optional custom texture property name used for alpha clipping; if not found, falls back to _BaseMap or _MainTex.")]
|
||||
public string alphaCutOffTextureName;
|
||||
|
||||
[Tooltip("If back facing triangles are ignored.Backfaces triangles are not visible but you may set this property to false to force highlight effects to act on those triangles as well.")]
|
||||
public bool cullBackFaces = true;
|
||||
|
||||
[Tooltip("Normals handling option:\nPreserve original: use original mesh normals.\nSmooth: average normals to produce a smoother outline/glow mesh based effect.\nReorient: recomputes normals based on vertex direction to centroid.")]
|
||||
public NormalsOption normalsOption;
|
||||
|
||||
public float fadeInDuration;
|
||||
public float fadeOutDuration;
|
||||
|
||||
[Tooltip("Fades out effects based on distance to camera")]
|
||||
public bool cameraDistanceFade;
|
||||
|
||||
[Tooltip("The closest distance particles can get to the camera before they fade from the camera's view.")]
|
||||
public float cameraDistanceFadeNear;
|
||||
|
||||
[Tooltip("The farthest distance particles can get away from the camera before they fade from the camera's view.")]
|
||||
public float cameraDistanceFadeFar = 1000;
|
||||
|
||||
[Tooltip("Keeps the outline/glow size unaffected by object distance.")]
|
||||
public bool constantWidth = true;
|
||||
|
||||
[Tooltip("Increases the screen coverage for the outline/glow to avoid cuts when using cloth or vertex shader that transform mesh vertices")]
|
||||
public int extraCoveragePixels;
|
||||
|
||||
[Tooltip("Minimum width when the constant width option is not used")]
|
||||
[Range(0, 1)]
|
||||
public float minimumWidth;
|
||||
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Intensity of the overlay effect. A value of 0 disables the overlay completely.")]
|
||||
public float overlay;
|
||||
public OverlayMode overlayMode = OverlayMode.WhenHighlighted;
|
||||
[ColorUsage(showAlpha: false, hdr: true)] public Color overlayColor = Color.yellow;
|
||||
public float overlayAnimationSpeed = 1f;
|
||||
[Range(0, 1)]
|
||||
public float overlayMinIntensity = 0.5f;
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Controls the blending or mix of the overlay color with the natural colors of the object.")]
|
||||
public float overlayBlending = 1.0f;
|
||||
[Tooltip("Optional overlay texture.")]
|
||||
public Texture2D overlayTexture;
|
||||
public TextureUVSpace overlayTextureUVSpace;
|
||||
public float overlayTextureScale = 1f;
|
||||
public Vector2 overlayTextureScrolling;
|
||||
public Visibility overlayVisibility = Visibility.Normal;
|
||||
[Tooltip("Controls the blend mode of the overlay effect.")]
|
||||
public OverlayBlendMode overlayBlendMode = OverlayBlendMode.AlphaBlending;
|
||||
|
||||
[Tooltip("Optional overlay pattern texture.")]
|
||||
public OverlayPattern overlayPattern = OverlayPattern.None;
|
||||
public Vector2 overlayPatternScrolling;
|
||||
[Tooltip("Scale of the overlay pattern")]
|
||||
[Range(1f, 100f)]
|
||||
public float overlayPatternScale = 10f;
|
||||
[Tooltip("Size/Thickness of the overlay pattern")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float overlayPatternSize = 0.15f;
|
||||
[Tooltip("Softness of the overlay pattern")]
|
||||
[Range(0.01f, 0.5f)]
|
||||
public float overlayPatternSoftness = 0.02f;
|
||||
[Tooltip("Rotation angle for the overlay pattern in degrees")]
|
||||
[Range(-180f, 180f)]
|
||||
public float overlayPatternRotation = 0f;
|
||||
|
||||
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Intensity of the outline. A value of 0 disables the outline completely.")]
|
||||
public float outline = 1f;
|
||||
[ColorUsage(true, true)] public Color outlineColor = Color.black;
|
||||
public ColorStyle outlineColorStyle = ColorStyle.SingleColor;
|
||||
[GradientUsage(hdr: true, ColorSpace.Linear)] public Gradient outlineGradient;
|
||||
public bool outlineGradientInLocalSpace;
|
||||
[Range(1, 3)]
|
||||
public int outlineBlurPasses = 2;
|
||||
public float outlineWidth = 0.45f;
|
||||
public QualityLevel outlineQuality = QualityLevel.High;
|
||||
public OutlineEdgeMode outlineEdgeMode = OutlineEdgeMode.Exterior;
|
||||
public float outlineEdgeThreshold = 0.995f;
|
||||
[Tooltip("Controls how quickly the outline effect scales down with distance when constant width is disabled. Lower values make the effect fade faster with distance.")]
|
||||
public float outlineDistanceScaleBias = 25f;
|
||||
public float outlineSharpness = 1f;
|
||||
[Range(1, 8)]
|
||||
[Tooltip("Reduces the quality of the outline but improves performance a bit.")]
|
||||
public int outlineDownsampling = 1;
|
||||
public ContourStyle outlineContourStyle = ContourStyle.AroundVisibleParts;
|
||||
public float outlineGradientKnee = 0.4f;
|
||||
public float outlineGradientPower = 8f;
|
||||
|
||||
[Tooltip("Enables stylized outline effect.")]
|
||||
public bool outlineStylized;
|
||||
[Tooltip("Pattern texture used for the stylized outline effect.")]
|
||||
public Texture2D outlinePattern;
|
||||
[Tooltip("Scale of the pattern texture.")]
|
||||
public float outlinePatternScale = 0.3f;
|
||||
[Tooltip("Threshold for the pattern texture.")]
|
||||
[Range(0, 1)]
|
||||
public float outlinePatternThreshold = 0.3f;
|
||||
[Tooltip("Distortion amount for the pattern texture.")]
|
||||
[Range(0, 1.5f)]
|
||||
public float outlinePatternDistortionAmount = 0.5f;
|
||||
[Tooltip("Stop motion scale for the distortion effect.")]
|
||||
public float outlinePatternStopMotionScale = 5f;
|
||||
[Tooltip("Distortion texture used for the stylized outline effect.")]
|
||||
public Texture2D outlinePatternDistortionTexture;
|
||||
[Tooltip("Adds a empty margin between the outline mesh and the effects")]
|
||||
[Range(0, 1)]
|
||||
public float padding;
|
||||
|
||||
[Tooltip("Makes the outline pixelated. A value of 0 disables pixelation. Higher values produce bigger pixels.")]
|
||||
[Range(0, 32)]
|
||||
public int outlinePixelation;
|
||||
|
||||
[Tooltip("Enables dashed outline effect.")]
|
||||
public bool outlineDashed;
|
||||
[Tooltip("Width of the dashed outline.")]
|
||||
[Range(0, 1)]
|
||||
public float outlineDashWidth = 0.5f;
|
||||
[Tooltip("Gap of the dashed outline.")]
|
||||
[Range(0, 1)]
|
||||
public float outlineDashGap = 0.3f;
|
||||
[Tooltip("Speed of the dashed outline.")]
|
||||
public float outlineDashSpeed = 2f;
|
||||
|
||||
public Visibility outlineVisibility = Visibility.Normal;
|
||||
[Tooltip("If enabled, this object won't combine the outline with other objects.")]
|
||||
public bool outlineIndependent;
|
||||
[Tooltip("Select the mask mode used with this effect.")]
|
||||
public MaskMode outlineMaskMode = MaskMode.Stencil;
|
||||
|
||||
[Range(0, 5)]
|
||||
[Tooltip("The intensity of the outer glow effect. A value of 0 disables the glow completely.")]
|
||||
public float glow;
|
||||
public float glowWidth = 0.4f;
|
||||
public QualityLevel glowQuality = QualityLevel.High;
|
||||
public BlurMethod glowBlurMethod = BlurMethod.Gaussian;
|
||||
public bool glowHighPrecision = true;
|
||||
[Tooltip("Controls how quickly the glow effect scales down with distance when constant width is disabled. Lower values make the effect fade faster with distance.")]
|
||||
public float glowDistanceScaleBias = 25f;
|
||||
[Range(1, 8)]
|
||||
[Tooltip("Reduces the quality of the glow but improves performance a bit.")]
|
||||
public int glowDownsampling = 2;
|
||||
[ColorUsage(true, true)] public Color glowHQColor = new Color(0.64f, 1f, 0f, 1f);
|
||||
[Tooltip("When enabled, outer glow renders with dithering. When disabled, glow appears as a solid color.")]
|
||||
[Range(0, 1)]
|
||||
public float glowDithering = 1;
|
||||
public GlowDitheringStyle glowDitheringStyle = GlowDitheringStyle.Pattern;
|
||||
[Tooltip("Seed for the dithering effect")]
|
||||
public float glowMagicNumber1 = 0.75f;
|
||||
[Tooltip("Another seed for the dithering effect that combines with first seed to create different patterns")]
|
||||
public float glowMagicNumber2 = 0.5f;
|
||||
public float glowAnimationSpeed = 1f;
|
||||
public Visibility glowVisibility = Visibility.Normal;
|
||||
public GlowBlendMode glowBlendMode = GlowBlendMode.Additive;
|
||||
[Tooltip("Blends glow passes one after another. If this option is disabled, glow passes won't overlap (in this case, make sure the glow pass 1 has a smaller offset than pass 2, etc.)")]
|
||||
public bool glowBlendPasses = true;
|
||||
public GlowPassData[] glowPasses;
|
||||
[Tooltip("Select the mask mode used with this effect.")]
|
||||
public MaskMode glowMaskMode = MaskMode.Stencil;
|
||||
|
||||
[Tooltip("Makes the glow pixelated. A value of 0 disables pixelation. Higher values produce bigger pixels.")]
|
||||
[Range(0, 32)]
|
||||
public int glowPixelation;
|
||||
|
||||
[Range(0, 5f)]
|
||||
[Tooltip("The intensity of the inner glow effect. A value of 0 disables the glow completely.")]
|
||||
public float innerGlow;
|
||||
[Range(0, 2)]
|
||||
public float innerGlowWidth = 1f;
|
||||
public float innerGlowPower = 1f;
|
||||
public InnerGlowBlendMode innerGlowBlendMode = InnerGlowBlendMode.Additive;
|
||||
[ColorUsage(true, true)] public Color innerGlowColor = Color.white;
|
||||
public Visibility innerGlowVisibility = Visibility.Normal;
|
||||
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Intensity of the focus effect. A value of 0 disables the focus effect completely.")]
|
||||
public float focus;
|
||||
[Tooltip("Color and transparency of the focus dim overlay.")]
|
||||
public Color focusColor = new Color(0, 0, 0, 0.9412f);
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Optional background blur intensity for the focus effect.")]
|
||||
public float focusBlur;
|
||||
[Range(1, 8)]
|
||||
[Tooltip("Downsampling factor for the focus blur. Higher values improve performance at the cost of quality.")]
|
||||
public int focusBlurDownsampling = 2;
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Desaturation intensity for the focus background. A value of 1 makes the background fully grayscale.")]
|
||||
public float focusDesaturation;
|
||||
|
||||
[Tooltip("Enables the targetFX effect. This effect draws an animated sprite over the object.")]
|
||||
public bool targetFX;
|
||||
[Tooltip("Style of the target FX effect.")]
|
||||
public TargetFXStyle targetFXStyle = TargetFXStyle.Texture;
|
||||
[Tooltip("Width of the frame when using Frame style.")]
|
||||
[Range(0.001f, 0.5f)]
|
||||
public float targetFXFrameWidth = 0.1f;
|
||||
[Tooltip("Length of the frame corners when using Frame style.")]
|
||||
[Range(0.1f, 1f)]
|
||||
public float targetFXCornerLength = 0.25f;
|
||||
[Tooltip("Minimum opacity of the frame when using Frame style.")]
|
||||
[Range(0, 1)]
|
||||
public float targetFXFrameMinOpacity;
|
||||
public Texture2D targetFXTexture;
|
||||
[ColorUsage(true, true)] public Color targetFXColor = Color.white;
|
||||
public float targetFXRotationSpeed = 50f;
|
||||
public float targetFXRotationAngle;
|
||||
public float targetFXInitialScale = 4f;
|
||||
public float targetFXEndScale = 1.5f;
|
||||
[Tooltip("Makes target scale relative to object renderer bounds.")]
|
||||
public bool targetFXScaleToRenderBounds;
|
||||
[Tooltip("Makes target FX effect square")]
|
||||
public bool targetFXSquare = true;
|
||||
[Tooltip("Places target FX sprite at the bottom of the highlighted object.")]
|
||||
public bool targetFXAlignToGround;
|
||||
[Tooltip("Max distance from the center of the highlighted object to the ground.")]
|
||||
public float targetFXGroundMaxDistance = 15f;
|
||||
public LayerMask targetFXGroundLayerMask = -1;
|
||||
[Tooltip("Fade out effect with altitude")]
|
||||
public float targetFXFadePower = 32;
|
||||
[Tooltip("Enable to render a single target FX effect at the center of the enclosing bounds")]
|
||||
public bool targetFXUseEnclosingBounds;
|
||||
[Tooltip("Optional world space offset for the position of the targetFX effect")]
|
||||
public Vector3 targetFXOffset;
|
||||
[Tooltip("If enabled, the target FX effect will be centered on the hit position")]
|
||||
public bool targetFxCenterOnHitPosition;
|
||||
[Tooltip("If enabled, the target FX effect will align to the hit normal")]
|
||||
public bool targetFxAlignToNormal;
|
||||
public float targetFXTransitionDuration = 0.5f;
|
||||
[Tooltip("0 = stay forever")]
|
||||
public float targetFXStayDuration = 1.5f;
|
||||
public Visibility targetFXVisibility = Visibility.AlwaysOnTop;
|
||||
[Tooltip("If the ground is transparent, the effect won't work. You can set this property to the altitude of the transparent ground to force the effect to render at this altitude.")]
|
||||
public float targetFXGroundMinAltitude = -1000;
|
||||
|
||||
[Tooltip("Enables the iconFX effect. This effect draws an animated object over the object.")]
|
||||
public bool iconFX;
|
||||
public IconAssetType iconFXAssetType;
|
||||
public GameObject iconFXPrefab;
|
||||
public Mesh iconFXMesh;
|
||||
[ColorUsage(true, true)] public Color iconFXLightColor = Color.white;
|
||||
[ColorUsage(true, true)] public Color iconFXDarkColor = Color.gray;
|
||||
public float iconFXRotationSpeed = 50f;
|
||||
public IconAnimationOption iconFXAnimationOption = IconAnimationOption.None;
|
||||
public float iconFXAnimationAmount = 0.1f;
|
||||
public float iconFXAnimationSpeed = 3f;
|
||||
public float iconFXScale = 1f;
|
||||
[Tooltip("Makes target scale relative to object renderer bounds.")]
|
||||
public bool iconFXScaleToRenderBounds;
|
||||
[Tooltip("Optional world space offset for the position of the iconFX effect")]
|
||||
public Vector3 iconFXOffset = new Vector3(0, 1, 0);
|
||||
public float iconFXTransitionDuration = 0.5f;
|
||||
[Tooltip("0 = stay forever")]
|
||||
public float iconFXStayDuration = 1.5f;
|
||||
|
||||
[Tooltip("Enables the label effect. This effect shows a text label over the object.")]
|
||||
public bool labelEnabled;
|
||||
[Tooltip("Screen Space renders the label as a 2D overlay. World Space renders the label in 3D, suitable for VR.")]
|
||||
public LabelRenderMode labelRenderMode = LabelRenderMode.ScreenSpace;
|
||||
[Tooltip("The text to display in the label")]
|
||||
public string labelText = "Label";
|
||||
[Tooltip("The size of the label text")]
|
||||
public float labelTextSize = 14;
|
||||
[ColorUsage(true, true)] public Color labelColor = Color.white;
|
||||
[Tooltip("The prefab to use for the label. Must contain a Canvas and TextMeshProUGUI component.")]
|
||||
public GameObject labelPrefab;
|
||||
public float labelVerticalOffset;
|
||||
public Vector2 labelViewportOffset;
|
||||
[Tooltip("The horizontal offset of the label with respect to the object bounds")]
|
||||
[FormerlySerializedAs("lineLength")]
|
||||
public float labelLineLength = 200;
|
||||
|
||||
[Tooltip("If enabled, the label will follow the cursor when hovering the object")]
|
||||
public bool labelFollowCursor = true;
|
||||
public LabelMode labelMode = LabelMode.WhenHighlighted;
|
||||
[Tooltip("If enabled, the label will be shown in editor mode (non playing)")]
|
||||
public bool labelShowInEditorMode = true;
|
||||
[Tooltip("Controls the alignment of the label relative to the target object on screen.")]
|
||||
public LabelAlignment labelAlignment = LabelAlignment.Auto;
|
||||
[Tooltip("Enables relative alignment based on the forward direction of the alignment transform.")]
|
||||
public bool labelRelativeAlignment;
|
||||
[Tooltip("The transform used for relative alignment direction.")]
|
||||
public Transform labelAlignmentTransform;
|
||||
|
||||
[Tooltip("Maximum distance from camera where the label is visible (units). Default 250.")]
|
||||
public float labelMaxDistance = 250f;
|
||||
[Tooltip("Distance at which label starts fading out (units). 0 disables fading.")]
|
||||
public float labelFadeStartDistance = 200f;
|
||||
[Tooltip("Enable distance-based scaling of label Canvas.")]
|
||||
public bool labelScaleByDistance;
|
||||
[Tooltip("Minimum scale when at max distance (0..1 typically). Default 1.")]
|
||||
public float labelScaleMin = 1f;
|
||||
[Tooltip("Maximum scale when near camera. Default 1.")]
|
||||
public float labelScaleMax = 1f;
|
||||
[Tooltip("Base scale of the label in World Space mode. Adjust to match desired apparent size.")]
|
||||
public float labelWorldSpaceScale = 0.01f;
|
||||
|
||||
[Tooltip("See-through mode for this Highlight Effect component.")]
|
||||
public SeeThroughMode seeThrough = SeeThroughMode.Never;
|
||||
[Tooltip("This mask setting let you specify which objects will be considered as occluders and cause the see-through effect for this Highlight Effect component. For example, you assign your walls to a different layer and specify that layer here, so only walls and not other objects, like ground or ceiling, will trigger the see-through effect.")]
|
||||
public LayerMask seeThroughOccluderMask = -1;
|
||||
[Tooltip("Uses stencil buffers to ensure pixel-accurate occlusion test. If this option is disabled, only physics raycasting is used to test for occlusion.")]
|
||||
public bool seeThroughOccluderMaskAccurate;
|
||||
[Tooltip("A multiplier for the occluder volume size which can be used to reduce the actual size of occluders when Highlight Effect checks if they're occluding this object.")]
|
||||
[Range(0.01f, 0.9f)] public float seeThroughOccluderThreshold = 0.4f;
|
||||
[Tooltip("The interval of time between occlusion tests.")]
|
||||
public float seeThroughOccluderCheckInterval = 1f;
|
||||
[Tooltip("If enabled, occlusion test is performed for each children element. If disabled, the bounds of all children is combined and a single occlusion test is performed for the combined bounds.")]
|
||||
public bool seeThroughOccluderCheckIndividualObjects;
|
||||
[Tooltip("Shows the see-through effect only if the occluder if at this 'offset' distance from the object.")]
|
||||
public float seeThroughDepthOffset;
|
||||
[Tooltip("Hides the see-through effect if the occluder is further than this distance from the object (0 = infinite)")]
|
||||
public float seeThroughMaxDepth;
|
||||
[Tooltip("Fade-out width near Max Depth (0 keeps hard clip)")]
|
||||
public float seeThroughFadeRange;
|
||||
[Range(0, 5f)] public float seeThroughIntensity = 0.8f;
|
||||
[Range(0, 1)] public float seeThroughTintAlpha = 0.5f;
|
||||
public Color seeThroughTintColor = Color.red;
|
||||
[Range(0, 1)] public float seeThroughNoise = 1f;
|
||||
[Range(0, 1)] public float seeThroughBorder;
|
||||
public Color seeThroughBorderColor = Color.black;
|
||||
public float seeThroughBorderWidth = 0.45f;
|
||||
[Tooltip("Only display the border instead of the full see-through effect.")]
|
||||
public bool seeThroughBorderOnly;
|
||||
[Tooltip("This option clears the stencil buffer after rendering the see-through effect which results in correct rendering order and supports other stencil-based effects that render afterwards.")]
|
||||
public bool seeThroughOrdered;
|
||||
[Tooltip("Optional see-through mask effect texture.")]
|
||||
public Texture2D seeThroughTexture;
|
||||
public TextureUVSpace seeThroughTextureUVSpace;
|
||||
public float seeThroughTextureScale = 1f;
|
||||
[Tooltip("The order by which children objects are rendered by the see-through effect")]
|
||||
public SeeThroughSortingMode seeThroughChildrenSortingMode = SeeThroughSortingMode.Default;
|
||||
|
||||
[Range(0, 1)] public float hitFxInitialIntensity;
|
||||
public HitFxMode hitFxMode = HitFxMode.Overlay;
|
||||
public HitFXTriggerMode hitFXTriggerMode = HitFXTriggerMode.Scripting;
|
||||
public float hitFxFadeOutDuration = 0.25f;
|
||||
[ColorUsage(true, true)] public Color hitFxColor = Color.white;
|
||||
public float hitFxRadius = 0.5f;
|
||||
|
||||
public void Load (HighlightEffect effect) {
|
||||
effect.effectGroup = effectGroup;
|
||||
effect.effectGroupLayer = effectGroupLayer;
|
||||
effect.effectNameFilter = effectNameFilter;
|
||||
effect.effectNameUseRegEx = effectNameUseRegEx;
|
||||
effect.excludeObjectsWithHighlightEffect = excludeObjectsWithHighlightEffect;
|
||||
effect.combineMeshes = combineMeshes;
|
||||
effect.alphaCutOff = alphaCutOff;
|
||||
effect.alphaCutOffTextureName = alphaCutOffTextureName;
|
||||
effect.cullBackFaces = cullBackFaces;
|
||||
effect.padding = padding;
|
||||
effect.normalsOption = normalsOption;
|
||||
effect.fadeInDuration = fadeInDuration;
|
||||
effect.fadeOutDuration = fadeOutDuration;
|
||||
effect.cameraDistanceFade = cameraDistanceFade;
|
||||
effect.cameraDistanceFadeFar = cameraDistanceFadeFar;
|
||||
effect.cameraDistanceFadeNear = cameraDistanceFadeNear;
|
||||
effect.constantWidth = constantWidth;
|
||||
effect.extraCoveragePixels = extraCoveragePixels;
|
||||
effect.minimumWidth = minimumWidth;
|
||||
|
||||
effect.overlay = overlay;
|
||||
effect.overlayMode = overlayMode;
|
||||
effect.overlayColor = overlayColor;
|
||||
effect.overlayAnimationSpeed = overlayAnimationSpeed;
|
||||
effect.overlayMinIntensity = overlayMinIntensity;
|
||||
effect.overlayBlending = overlayBlending;
|
||||
effect.overlayTexture = overlayTexture;
|
||||
effect.overlayTextureUVSpace = overlayTextureUVSpace;
|
||||
effect.overlayTextureScale = overlayTextureScale;
|
||||
effect.overlayTextureScrolling = overlayTextureScrolling;
|
||||
effect.overlayVisibility = overlayVisibility;
|
||||
effect.overlayBlendMode = overlayBlendMode;
|
||||
effect.overlayPattern = overlayPattern;
|
||||
effect.overlayPatternScrolling = overlayPatternScrolling;
|
||||
effect.overlayPatternScale = overlayPatternScale;
|
||||
effect.overlayPatternSize = overlayPatternSize;
|
||||
effect.overlayPatternSoftness = overlayPatternSoftness;
|
||||
effect.overlayPatternRotation = overlayPatternRotation;
|
||||
|
||||
effect.outline = outline;
|
||||
effect.outlineColor = outlineColor;
|
||||
effect.outlineColorStyle = outlineColorStyle;
|
||||
effect.outlineGradient = outlineGradient;
|
||||
effect.outlineGradientInLocalSpace = outlineGradientInLocalSpace;
|
||||
effect.outlineWidth = outlineWidth;
|
||||
effect.outlineBlurPasses = outlineBlurPasses;
|
||||
effect.outlineQuality = outlineQuality;
|
||||
effect.outlineEdgeMode = outlineEdgeMode;
|
||||
effect.outlineEdgeThreshold = outlineEdgeThreshold;
|
||||
effect.outlineDistanceScaleBias = outlineDistanceScaleBias;
|
||||
effect.outlineSharpness = outlineSharpness;
|
||||
effect.outlineDownsampling = outlineDownsampling;
|
||||
effect.outlineVisibility = outlineVisibility;
|
||||
effect.outlineIndependent = outlineIndependent;
|
||||
effect.outlineContourStyle = outlineContourStyle;
|
||||
effect.outlineMaskMode = outlineMaskMode;
|
||||
effect.outlineStylized = outlineStylized;
|
||||
effect.outlinePattern = outlinePattern;
|
||||
effect.outlinePatternScale = outlinePatternScale;
|
||||
effect.outlinePatternThreshold = outlinePatternThreshold;
|
||||
effect.outlinePatternDistortionTexture = outlinePatternDistortionTexture;
|
||||
effect.outlinePatternDistortionAmount = outlinePatternDistortionAmount;
|
||||
effect.outlinePatternStopMotionScale = outlinePatternStopMotionScale;
|
||||
effect.outlineDashed = outlineDashed;
|
||||
effect.outlineDashWidth = outlineDashWidth;
|
||||
effect.outlineDashGap = outlineDashGap;
|
||||
effect.outlineDashSpeed = outlineDashSpeed;
|
||||
effect.outlineGradientKnee = outlineGradientKnee;
|
||||
effect.outlineGradientPower = outlineGradientPower;
|
||||
effect.outlinePixelation = outlinePixelation;
|
||||
|
||||
effect.glow = glow;
|
||||
effect.glowWidth = glowWidth;
|
||||
effect.glowQuality = glowQuality;
|
||||
effect.glowBlurMethod = glowBlurMethod;
|
||||
effect.glowHighPrecision = glowHighPrecision;
|
||||
effect.glowDistanceScaleBias = glowDistanceScaleBias;
|
||||
effect.glowDownsampling = glowDownsampling;
|
||||
effect.glowHQColor = glowHQColor;
|
||||
effect.glowDithering = glowDithering;
|
||||
effect.glowDitheringStyle = glowDitheringStyle;
|
||||
effect.glowMagicNumber1 = glowMagicNumber1;
|
||||
effect.glowMagicNumber2 = glowMagicNumber2;
|
||||
effect.glowAnimationSpeed = glowAnimationSpeed;
|
||||
effect.glowVisibility = glowVisibility;
|
||||
effect.glowBlendMode = glowBlendMode;
|
||||
effect.glowBlendPasses = glowBlendPasses;
|
||||
effect.glowPasses = GetGlowPassesCopy(glowPasses);
|
||||
effect.glowMaskMode = glowMaskMode;
|
||||
effect.glowPixelation = glowPixelation;
|
||||
|
||||
effect.innerGlow = innerGlow;
|
||||
effect.innerGlowWidth = innerGlowWidth;
|
||||
effect.innerGlowPower = innerGlowPower;
|
||||
effect.innerGlowColor = innerGlowColor;
|
||||
effect.innerGlowBlendMode = innerGlowBlendMode;
|
||||
effect.innerGlowVisibility = innerGlowVisibility;
|
||||
|
||||
effect.focus = focus;
|
||||
effect.focusColor = focusColor;
|
||||
effect.focusBlur = focusBlur;
|
||||
effect.focusBlurDownsampling = focusBlurDownsampling;
|
||||
effect.focusDesaturation = focusDesaturation;
|
||||
|
||||
effect.targetFX = targetFX;
|
||||
effect.targetFXColor = targetFXColor;
|
||||
effect.targetFXInitialScale = targetFXInitialScale;
|
||||
effect.targetFXEndScale = targetFXEndScale;
|
||||
effect.targetFXScaleToRenderBounds = targetFXScaleToRenderBounds;
|
||||
effect.targetFXAlignToGround = targetFXAlignToGround;
|
||||
effect.targetFXGroundMaxDistance = targetFXGroundMaxDistance;
|
||||
effect.targetFXGroundLayerMask = targetFXGroundLayerMask;
|
||||
effect.targetFXFadePower = targetFXFadePower;
|
||||
effect.targetFXRotationSpeed = targetFXRotationSpeed;
|
||||
effect.targetFXRotationAngle = targetFXRotationAngle;
|
||||
effect.targetFXStayDuration = targetFXStayDuration;
|
||||
effect.targetFXTexture = targetFXTexture;
|
||||
effect.targetFXTransitionDuration = targetFXTransitionDuration;
|
||||
effect.targetFXVisibility = targetFXVisibility;
|
||||
effect.targetFXUseEnclosingBounds = targetFXUseEnclosingBounds;
|
||||
effect.targetFXSquare = targetFXSquare;
|
||||
effect.targetFXOffset = targetFXOffset;
|
||||
effect.targetFxCenterOnHitPosition = targetFxCenterOnHitPosition;
|
||||
effect.targetFxAlignToNormal = targetFxAlignToNormal;
|
||||
effect.targetFXStyle = targetFXStyle;
|
||||
effect.targetFXFrameWidth = targetFXFrameWidth;
|
||||
effect.targetFXCornerLength = targetFXCornerLength;
|
||||
effect.targetFXFrameMinOpacity = targetFXFrameMinOpacity;
|
||||
effect.targetFXGroundMinAltitude = targetFXGroundMinAltitude;
|
||||
|
||||
effect.iconFX = iconFX;
|
||||
effect.iconFXAssetType = iconFXAssetType;
|
||||
effect.iconFXPrefab = iconFXPrefab;
|
||||
effect.iconFXMesh = iconFXMesh;
|
||||
effect.iconFXLightColor = iconFXLightColor;
|
||||
effect.iconFXDarkColor = iconFXDarkColor;
|
||||
effect.iconFXAnimationOption = iconFXAnimationOption;
|
||||
effect.iconFXAnimationAmount = iconFXAnimationAmount;
|
||||
effect.iconFXAnimationSpeed = iconFXAnimationSpeed;
|
||||
effect.iconFXScale = iconFXScale;
|
||||
effect.iconFXScaleToRenderBounds = iconFXScaleToRenderBounds;
|
||||
effect.iconFXOffset = iconFXOffset;
|
||||
effect.iconFXRotationSpeed = iconFXRotationSpeed;
|
||||
effect.iconFXStayDuration = iconFXStayDuration;
|
||||
effect.iconFXTransitionDuration = iconFXTransitionDuration;
|
||||
|
||||
effect.seeThrough = seeThrough;
|
||||
effect.seeThroughOccluderMask = seeThroughOccluderMask;
|
||||
effect.seeThroughOccluderMaskAccurate = seeThroughOccluderMaskAccurate;
|
||||
effect.seeThroughOccluderThreshold = seeThroughOccluderThreshold;
|
||||
effect.seeThroughOccluderCheckInterval = seeThroughOccluderCheckInterval;
|
||||
effect.seeThroughOccluderCheckIndividualObjects = seeThroughOccluderCheckIndividualObjects;
|
||||
effect.seeThroughIntensity = seeThroughIntensity;
|
||||
effect.seeThroughTintAlpha = seeThroughTintAlpha;
|
||||
effect.seeThroughTintColor = seeThroughTintColor;
|
||||
effect.seeThroughNoise = seeThroughNoise;
|
||||
effect.seeThroughBorder = seeThroughBorder;
|
||||
effect.seeThroughBorderColor = seeThroughBorderColor;
|
||||
effect.seeThroughBorderWidth = seeThroughBorderWidth;
|
||||
effect.seeThroughBorderOnly = seeThroughBorderOnly;
|
||||
effect.seeThroughDepthOffset = seeThroughDepthOffset;
|
||||
effect.seeThroughMaxDepth = seeThroughMaxDepth;
|
||||
effect.seeThroughFadeRange = seeThroughFadeRange;
|
||||
effect.seeThroughOrdered = seeThroughOrdered;
|
||||
effect.seeThroughTexture = seeThroughTexture;
|
||||
effect.seeThroughTextureScale = seeThroughTextureScale;
|
||||
effect.seeThroughTextureUVSpace = seeThroughTextureUVSpace;
|
||||
effect.seeThroughChildrenSortingMode = seeThroughChildrenSortingMode;
|
||||
|
||||
effect.hitFxInitialIntensity = hitFxInitialIntensity;
|
||||
effect.hitFxMode = hitFxMode;
|
||||
effect.hitFXTriggerMode = hitFXTriggerMode;
|
||||
effect.hitFxFadeOutDuration = hitFxFadeOutDuration;
|
||||
effect.hitFxColor = hitFxColor;
|
||||
effect.hitFxRadius = hitFxRadius;
|
||||
|
||||
effect.labelEnabled = labelEnabled;
|
||||
effect.labelRenderMode = labelRenderMode;
|
||||
effect.labelText = labelText;
|
||||
effect.labelTextSize = labelTextSize;
|
||||
effect.labelColor = labelColor;
|
||||
effect.labelPrefab = labelPrefab;
|
||||
effect.labelVerticalOffset = labelVerticalOffset;
|
||||
effect.labelViewportOffset = labelViewportOffset;
|
||||
effect.labelLineLength = labelLineLength;
|
||||
effect.labelMaxDistance = labelMaxDistance;
|
||||
effect.labelFadeStartDistance = labelFadeStartDistance;
|
||||
effect.labelScaleByDistance = labelScaleByDistance;
|
||||
effect.labelScaleMin = labelScaleMin;
|
||||
effect.labelScaleMax = labelScaleMax;
|
||||
effect.labelWorldSpaceScale = labelWorldSpaceScale;
|
||||
effect.labelFollowCursor = labelFollowCursor;
|
||||
effect.labelMode = labelMode;
|
||||
effect.labelAlignment = labelAlignment;
|
||||
effect.labelShowInEditorMode = labelShowInEditorMode;
|
||||
effect.labelRelativeAlignment = labelRelativeAlignment;
|
||||
effect.labelAlignmentTransform = labelAlignmentTransform;
|
||||
|
||||
effect.UpdateMaterialProperties();
|
||||
}
|
||||
|
||||
public void Save (HighlightEffect effect) {
|
||||
effectGroup = effect.effectGroup;
|
||||
effectGroupLayer = effect.effectGroupLayer;
|
||||
effectNameFilter = effect.effectNameFilter;
|
||||
effectNameUseRegEx = effect.effectNameUseRegEx;
|
||||
excludeObjectsWithHighlightEffect = effect.excludeObjectsWithHighlightEffect;
|
||||
combineMeshes = effect.combineMeshes;
|
||||
alphaCutOff = effect.alphaCutOff;
|
||||
alphaCutOffTextureName = effect.alphaCutOffTextureName;
|
||||
cullBackFaces = effect.cullBackFaces;
|
||||
padding = effect.padding;
|
||||
normalsOption = effect.normalsOption;
|
||||
fadeInDuration = effect.fadeInDuration;
|
||||
fadeOutDuration = effect.fadeOutDuration;
|
||||
cameraDistanceFade = effect.cameraDistanceFade;
|
||||
cameraDistanceFadeFar = effect.cameraDistanceFadeFar;
|
||||
cameraDistanceFadeNear = effect.cameraDistanceFadeNear;
|
||||
constantWidth = effect.constantWidth;
|
||||
extraCoveragePixels = effect.extraCoveragePixels;
|
||||
minimumWidth = effect.minimumWidth;
|
||||
|
||||
overlay = effect.overlay;
|
||||
overlayMode = effect.overlayMode;
|
||||
overlayColor = effect.overlayColor;
|
||||
overlayAnimationSpeed = effect.overlayAnimationSpeed;
|
||||
overlayMinIntensity = effect.overlayMinIntensity;
|
||||
overlayBlending = effect.overlayBlending;
|
||||
overlayTexture = effect.overlayTexture;
|
||||
overlayTextureUVSpace = effect.overlayTextureUVSpace;
|
||||
overlayTextureScale = effect.overlayTextureScale;
|
||||
overlayTextureScrolling = effect.overlayTextureScrolling;
|
||||
overlayVisibility = effect.overlayVisibility;
|
||||
overlayBlendMode = effect.overlayBlendMode;
|
||||
overlayPattern = effect.overlayPattern;
|
||||
overlayPatternScrolling = effect.overlayPatternScrolling;
|
||||
overlayPatternScale = effect.overlayPatternScale;
|
||||
overlayPatternSize = effect.overlayPatternSize;
|
||||
overlayPatternSoftness = effect.overlayPatternSoftness;
|
||||
overlayPatternRotation = effect.overlayPatternRotation;
|
||||
|
||||
outline = effect.outline;
|
||||
outlineColor = effect.outlineColor;
|
||||
outlineColorStyle = effect.outlineColorStyle;
|
||||
outlineGradient = effect.outlineGradient;
|
||||
outlineGradientInLocalSpace = effect.outlineGradientInLocalSpace;
|
||||
outlineWidth = effect.outlineWidth;
|
||||
outlineBlurPasses = effect.outlineBlurPasses;
|
||||
outlineQuality = effect.outlineQuality;
|
||||
outlineEdgeMode = effect.outlineEdgeMode;
|
||||
outlineEdgeThreshold = effect.outlineEdgeThreshold;
|
||||
outlineDistanceScaleBias = effect.outlineDistanceScaleBias;
|
||||
outlineSharpness = effect.outlineSharpness;
|
||||
outlineDownsampling = effect.outlineDownsampling;
|
||||
outlineVisibility = effect.outlineVisibility;
|
||||
outlineIndependent = effect.outlineIndependent;
|
||||
outlineContourStyle = effect.outlineContourStyle;
|
||||
outlineMaskMode = effect.outlineMaskMode;
|
||||
outlineStylized = effect.outlineStylized;
|
||||
outlinePattern = effect.outlinePattern;
|
||||
outlinePatternScale = effect.outlinePatternScale;
|
||||
outlinePatternThreshold = effect.outlinePatternThreshold;
|
||||
outlinePatternDistortionTexture = effect.outlinePatternDistortionTexture;
|
||||
outlinePatternDistortionAmount = effect.outlinePatternDistortionAmount;
|
||||
outlinePatternStopMotionScale = effect.outlinePatternStopMotionScale;
|
||||
outlineDashed = effect.outlineDashed;
|
||||
outlineDashWidth = effect.outlineDashWidth;
|
||||
outlineDashGap = effect.outlineDashGap;
|
||||
outlineDashSpeed = effect.outlineDashSpeed;
|
||||
outlineGradientKnee = effect.outlineGradientKnee;
|
||||
outlineGradientPower = effect.outlineGradientPower;
|
||||
outlinePixelation = effect.outlinePixelation;
|
||||
|
||||
glow = effect.glow;
|
||||
glowWidth = effect.glowWidth;
|
||||
glowQuality = effect.glowQuality;
|
||||
glowHighPrecision = effect.glowHighPrecision;
|
||||
glowBlurMethod = effect.glowBlurMethod;
|
||||
glowDistanceScaleBias = effect.glowDistanceScaleBias;
|
||||
glowDownsampling = effect.glowDownsampling;
|
||||
glowHQColor = effect.glowHQColor;
|
||||
glowDithering = effect.glowDithering;
|
||||
glowDitheringStyle = effect.glowDitheringStyle;
|
||||
glowMagicNumber1 = effect.glowMagicNumber1;
|
||||
glowMagicNumber2 = effect.glowMagicNumber2;
|
||||
glowAnimationSpeed = effect.glowAnimationSpeed;
|
||||
glowVisibility = effect.glowVisibility;
|
||||
glowBlendMode = effect.glowBlendMode;
|
||||
glowBlendPasses = effect.glowBlendPasses;
|
||||
glowPasses = GetGlowPassesCopy(effect.glowPasses);
|
||||
glowMaskMode = effect.glowMaskMode;
|
||||
glowPixelation = effect.glowPixelation;
|
||||
|
||||
innerGlow = effect.innerGlow;
|
||||
innerGlowWidth = effect.innerGlowWidth;
|
||||
innerGlowPower = effect.innerGlowPower;
|
||||
innerGlowColor = effect.innerGlowColor;
|
||||
innerGlowBlendMode = effect.innerGlowBlendMode;
|
||||
innerGlowVisibility = effect.innerGlowVisibility;
|
||||
|
||||
focus = effect.focus;
|
||||
focusColor = effect.focusColor;
|
||||
focusBlur = effect.focusBlur;
|
||||
focusBlurDownsampling = effect.focusBlurDownsampling;
|
||||
focusDesaturation = effect.focusDesaturation;
|
||||
|
||||
targetFX = effect.targetFX;
|
||||
targetFXColor = effect.targetFXColor;
|
||||
targetFXInitialScale = effect.targetFXInitialScale;
|
||||
targetFXEndScale = effect.targetFXEndScale;
|
||||
targetFXScaleToRenderBounds = effect.targetFXScaleToRenderBounds;
|
||||
targetFXAlignToGround = effect.targetFXAlignToGround;
|
||||
targetFXGroundMaxDistance = effect.targetFXGroundMaxDistance;
|
||||
targetFXGroundLayerMask = effect.targetFXGroundLayerMask;
|
||||
targetFXFadePower = effect.targetFXFadePower;
|
||||
targetFXRotationSpeed = effect.targetFXRotationSpeed;
|
||||
targetFXRotationAngle = effect.targetFXRotationAngle;
|
||||
targetFXStayDuration = effect.targetFXStayDuration;
|
||||
targetFXTexture = effect.targetFXTexture;
|
||||
targetFXTransitionDuration = effect.targetFXTransitionDuration;
|
||||
targetFXVisibility = effect.targetFXVisibility;
|
||||
targetFXUseEnclosingBounds = effect.targetFXUseEnclosingBounds;
|
||||
targetFXSquare = effect.targetFXSquare;
|
||||
targetFXOffset = effect.targetFXOffset;
|
||||
targetFxCenterOnHitPosition = effect.targetFxCenterOnHitPosition;
|
||||
targetFxAlignToNormal = effect.targetFxAlignToNormal;
|
||||
targetFXStyle = effect.targetFXStyle;
|
||||
targetFXFrameWidth = effect.targetFXFrameWidth;
|
||||
targetFXCornerLength = effect.targetFXCornerLength;
|
||||
targetFXFrameMinOpacity = effect.targetFXFrameMinOpacity;
|
||||
targetFXGroundMinAltitude = effect.targetFXGroundMinAltitude;
|
||||
|
||||
iconFX = effect.iconFX;
|
||||
iconFXAssetType = effect.iconFXAssetType;
|
||||
iconFXPrefab = effect.iconFXPrefab;
|
||||
iconFXMesh = effect.iconFXMesh;
|
||||
iconFXLightColor = effect.iconFXLightColor;
|
||||
iconFXDarkColor = effect.iconFXDarkColor;
|
||||
iconFXAnimationOption = effect.iconFXAnimationOption;
|
||||
iconFXAnimationAmount = effect.iconFXAnimationAmount;
|
||||
iconFXAnimationSpeed = effect.iconFXAnimationSpeed;
|
||||
iconFXScaleToRenderBounds = effect.iconFXScaleToRenderBounds;
|
||||
iconFXScale = effect.iconFXScale;
|
||||
iconFXOffset = effect.iconFXOffset;
|
||||
iconFXRotationSpeed = effect.iconFXRotationSpeed;
|
||||
iconFXStayDuration = effect.iconFXStayDuration;
|
||||
iconFXTransitionDuration = effect.iconFXTransitionDuration;
|
||||
|
||||
seeThrough = effect.seeThrough;
|
||||
seeThroughOccluderMask = effect.seeThroughOccluderMask;
|
||||
seeThroughOccluderMaskAccurate = effect.seeThroughOccluderMaskAccurate;
|
||||
seeThroughOccluderThreshold = effect.seeThroughOccluderThreshold;
|
||||
seeThroughOccluderCheckInterval = effect.seeThroughOccluderCheckInterval;
|
||||
seeThroughOccluderCheckIndividualObjects = effect.seeThroughOccluderCheckIndividualObjects;
|
||||
seeThroughIntensity = effect.seeThroughIntensity;
|
||||
seeThroughTintAlpha = effect.seeThroughTintAlpha;
|
||||
seeThroughTintColor = effect.seeThroughTintColor;
|
||||
seeThroughNoise = effect.seeThroughNoise;
|
||||
seeThroughBorder = effect.seeThroughBorder;
|
||||
seeThroughBorderColor = effect.seeThroughBorderColor;
|
||||
seeThroughBorderWidth = effect.seeThroughBorderWidth;
|
||||
seeThroughDepthOffset = effect.seeThroughDepthOffset;
|
||||
seeThroughBorderOnly = effect.seeThroughBorderOnly;
|
||||
seeThroughMaxDepth = effect.seeThroughMaxDepth;
|
||||
seeThroughFadeRange = effect.seeThroughFadeRange;
|
||||
seeThroughOrdered = effect.seeThroughOrdered;
|
||||
seeThroughTexture = effect.seeThroughTexture;
|
||||
seeThroughTextureScale = effect.seeThroughTextureScale;
|
||||
seeThroughTextureUVSpace = effect.seeThroughTextureUVSpace;
|
||||
seeThroughChildrenSortingMode = effect.seeThroughChildrenSortingMode;
|
||||
|
||||
hitFxInitialIntensity = effect.hitFxInitialIntensity;
|
||||
hitFxMode = effect.hitFxMode;
|
||||
hitFXTriggerMode = effect.hitFXTriggerMode;
|
||||
hitFxFadeOutDuration = effect.hitFxFadeOutDuration;
|
||||
hitFxColor = effect.hitFxColor;
|
||||
hitFxRadius = effect.hitFxRadius;
|
||||
|
||||
labelEnabled = effect.labelEnabled;
|
||||
labelRenderMode = effect.labelRenderMode;
|
||||
labelText = effect.labelText;
|
||||
labelTextSize = effect.labelTextSize;
|
||||
labelColor = effect.labelColor;
|
||||
labelPrefab = effect.labelPrefab;
|
||||
labelVerticalOffset = effect.labelVerticalOffset;
|
||||
labelViewportOffset = effect.labelViewportOffset;
|
||||
labelLineLength = effect.labelLineLength;
|
||||
labelMaxDistance = effect.labelMaxDistance;
|
||||
labelFadeStartDistance = effect.labelFadeStartDistance;
|
||||
labelScaleByDistance = effect.labelScaleByDistance;
|
||||
labelScaleMin = effect.labelScaleMin;
|
||||
labelScaleMax = effect.labelScaleMax;
|
||||
labelWorldSpaceScale = effect.labelWorldSpaceScale;
|
||||
labelFollowCursor = effect.labelFollowCursor;
|
||||
labelMode = effect.labelMode;
|
||||
labelAlignment = effect.labelAlignment;
|
||||
labelShowInEditorMode = effect.labelShowInEditorMode;
|
||||
labelRelativeAlignment = effect.labelRelativeAlignment;
|
||||
labelAlignmentTransform = effect.labelAlignmentTransform;
|
||||
}
|
||||
|
||||
GlowPassData[] GetGlowPassesCopy (GlowPassData[] glowPasses) {
|
||||
if (glowPasses == null) {
|
||||
return new GlowPassData[0];
|
||||
}
|
||||
GlowPassData[] pd = new GlowPassData[glowPasses.Length];
|
||||
for (int k = 0; k < glowPasses.Length; k++) {
|
||||
pd[k].alpha = glowPasses[k].alpha;
|
||||
pd[k].color = glowPasses[k].color;
|
||||
pd[k].offset = glowPasses[k].offset;
|
||||
}
|
||||
return pd;
|
||||
}
|
||||
|
||||
public void OnValidate () {
|
||||
outlineGradientKnee = Mathf.Max(0f, outlineGradientKnee);
|
||||
outlineGradientPower = Mathf.Max(0f, outlineGradientPower);
|
||||
outlineEdgeThreshold = Mathf.Clamp01(outlineEdgeThreshold);
|
||||
outlineSharpness = Mathf.Max(outlineSharpness, 1f);
|
||||
outlineDistanceScaleBias = Mathf.Max(1, outlineDistanceScaleBias);
|
||||
extraCoveragePixels = Mathf.Max(0, extraCoveragePixels);
|
||||
glowWidth = Mathf.Max(0, glowWidth);
|
||||
glowAnimationSpeed = Mathf.Max(0, glowAnimationSpeed);
|
||||
glowDistanceScaleBias = Mathf.Max(1, glowDistanceScaleBias);
|
||||
outlineDistanceScaleBias = Mathf.Max(1, outlineDistanceScaleBias);
|
||||
overlayAnimationSpeed = Mathf.Max(0, overlayAnimationSpeed);
|
||||
innerGlowPower = Mathf.Max(1f, innerGlowPower);
|
||||
seeThroughDepthOffset = Mathf.Max(0, seeThroughDepthOffset);
|
||||
seeThroughMaxDepth = Mathf.Max(0, seeThroughMaxDepth);
|
||||
seeThroughFadeRange = Mathf.Max(0, seeThroughFadeRange);
|
||||
seeThroughBorderWidth = Mathf.Max(0, seeThroughBorderWidth);
|
||||
targetFXFadePower = Mathf.Max(0, targetFXFadePower);
|
||||
cameraDistanceFadeNear = Mathf.Max(0, cameraDistanceFadeNear);
|
||||
cameraDistanceFadeFar = Mathf.Max(0, cameraDistanceFadeFar);
|
||||
iconFXScale = Mathf.Max(0, iconFXScale);
|
||||
iconFXAnimationAmount = Mathf.Max(0, iconFXAnimationAmount);
|
||||
iconFXAnimationSpeed = Mathf.Max(0, iconFXAnimationSpeed);
|
||||
outlinePatternScale = Mathf.Max(0, outlinePatternScale);
|
||||
outlinePatternDistortionAmount = Mathf.Max(0, outlinePatternDistortionAmount);
|
||||
outlinePatternThreshold = Mathf.Max(0, outlinePatternThreshold);
|
||||
outlinePatternStopMotionScale = Mathf.Max(1, outlinePatternStopMotionScale);
|
||||
if (glowPasses == null || glowPasses.Length == 0) {
|
||||
glowPasses = new GlowPassData[4];
|
||||
glowPasses[0] = new GlowPassData() { offset = 4, alpha = 0.1f, color = new Color(0.64f, 1f, 0f, 1f) };
|
||||
glowPasses[1] = new GlowPassData() { offset = 3, alpha = 0.2f, color = new Color(0.64f, 1f, 0f, 1f) };
|
||||
glowPasses[2] = new GlowPassData() { offset = 2, alpha = 0.3f, color = new Color(0.64f, 1f, 0f, 1f) };
|
||||
glowPasses[3] = new GlowPassData() { offset = 1, alpha = 0.4f, color = new Color(0.64f, 1f, 0f, 1f) };
|
||||
}
|
||||
if (labelPrefab == null) {
|
||||
labelPrefab = Resources.Load<GameObject>("HighlightPlus/Label");
|
||||
}
|
||||
labelLineLength = Mathf.Max(0, labelLineLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e9253636bf2648bd813257f451f8486
|
||||
timeCreated: 1549831900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/HighlightPlus/Runtime/Scripts/HighlightRoot.cs
Normal file
7
Assets/HighlightPlus/Runtime/Scripts/HighlightRoot.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public class HighlightRoot : MonoBehaviour {
|
||||
}
|
||||
}
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/HighlightRoot.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/HighlightRoot.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 802672b6bc327458dae0bb9eee8c9ca2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public struct MeshData {
|
||||
public Renderer renderer;
|
||||
public int subMeshCount;
|
||||
}
|
||||
|
||||
public enum OccluderMode {
|
||||
BlocksSeeThrough,
|
||||
TriggersSeeThrough
|
||||
}
|
||||
|
||||
public enum DetectionMethod {
|
||||
Stencil = 0,
|
||||
RayCast = 1
|
||||
}
|
||||
[ExecuteInEditMode]
|
||||
public class HighlightSeeThroughOccluder : MonoBehaviour {
|
||||
|
||||
public OccluderMode mode = OccluderMode.BlocksSeeThrough;
|
||||
|
||||
public DetectionMethod detectionMethod = DetectionMethod.Stencil;
|
||||
|
||||
[NonSerialized]
|
||||
public MeshData[] meshData;
|
||||
|
||||
List<Renderer> rr;
|
||||
|
||||
void OnEnable () {
|
||||
if (gameObject.activeInHierarchy) {
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
void Init() {
|
||||
|
||||
if (mode == OccluderMode.BlocksSeeThrough && detectionMethod == DetectionMethod.RayCast) {
|
||||
HighlightEffect.RegisterOccluder(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rr == null) {
|
||||
rr = new List<Renderer>();
|
||||
} else {
|
||||
rr.Clear();
|
||||
}
|
||||
GetComponentsInChildren(rr);
|
||||
int rrCount = rr.Count;
|
||||
meshData = new MeshData[rrCount];
|
||||
for (int k = 0; k < rrCount; k++) {
|
||||
meshData[k].renderer = rr[k];
|
||||
meshData[k].subMeshCount = 1;
|
||||
if (rr[k] is MeshRenderer) {
|
||||
MeshFilter mf = rr[k].GetComponent<MeshFilter>();
|
||||
if (mf != null && mf.sharedMesh != null) {
|
||||
meshData[k].subMeshCount = mf.sharedMesh.subMeshCount;
|
||||
}
|
||||
} else if (rr[k] is SkinnedMeshRenderer) {
|
||||
SkinnedMeshRenderer smr = (SkinnedMeshRenderer)rr[k];
|
||||
if (smr.sharedMesh != null) {
|
||||
meshData[k].subMeshCount = smr.sharedMesh.subMeshCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rrCount > 0) {
|
||||
HighlightEffect.RegisterOccluder(this);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
HighlightEffect.UnregisterOccluder(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dd965ceab19c4729a9dabd8aeb2972a
|
||||
timeCreated: 1542876337
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
553
Assets/HighlightPlus/Runtime/Scripts/HighlightTrigger.cs
Normal file
553
Assets/HighlightPlus/Runtime/Scripts/HighlightTrigger.cs
Normal file
@@ -0,0 +1,553 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public enum TriggerMode {
|
||||
ColliderEventsOnlyOnThisObject = 0,
|
||||
RaycastOnThisObjectAndChildren = 1,
|
||||
Volume = 2
|
||||
}
|
||||
|
||||
public enum RayCastSource {
|
||||
MousePosition = 0,
|
||||
CameraDirection = 1
|
||||
}
|
||||
|
||||
|
||||
[RequireComponent(typeof(HighlightEffect))]
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL("https://kronnect.com/docs/highlight-plus/")]
|
||||
public class HighlightTrigger : MonoBehaviour {
|
||||
|
||||
[Tooltip("Enables highlight when pointer is over this object.")]
|
||||
public bool highlightOnHover = true;
|
||||
[Tooltip("Used to trigger automatic highlighting including children objects.")]
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
public TriggerMode triggerMode = TriggerMode.RaycastOnThisObjectAndChildren;
|
||||
#else
|
||||
public TriggerMode triggerMode = TriggerMode.ColliderEventsOnlyOnThisObject;
|
||||
#endif
|
||||
public Camera raycastCamera;
|
||||
public RayCastSource raycastSource = RayCastSource.MousePosition;
|
||||
public LayerMask raycastLayerMask = -1;
|
||||
[Tooltip("Objects in this layer mask will block the highlight if they are between the camera and the target.")]
|
||||
public LayerMask blockingLayerMask = -1;
|
||||
[Tooltip("Minimum distance for target.")]
|
||||
public float minDistance;
|
||||
[Tooltip("Maximum distance for target. 0 = infinity")]
|
||||
public float maxDistance;
|
||||
[Tooltip("Blocks interaction if pointer is over an UI element")]
|
||||
public bool respectUI = true;
|
||||
[Tooltip("Unhighlights the object when the pointer is over a UI element")]
|
||||
public bool unhighlightOnUI;
|
||||
public LayerMask volumeLayerMask;
|
||||
|
||||
const int MAX_RAYCAST_HITS = 100;
|
||||
|
||||
|
||||
[Tooltip("If the object will be selected by clicking with mouse or tapping on it.")]
|
||||
public bool selectOnClick;
|
||||
[Tooltip("Profile to use when object is selected by clicking on it.")]
|
||||
public HighlightProfile selectedProfile;
|
||||
[Tooltip("Profile to use whtn object is selected and highlighted.")]
|
||||
public HighlightProfile selectedAndHighlightedProfile;
|
||||
[Tooltip("Automatically deselects any other selected object prior selecting this one")]
|
||||
public bool singleSelection;
|
||||
[Tooltip("Toggles selection on/off when clicking object")]
|
||||
public bool toggle;
|
||||
[Tooltip("Keeps current selection when clicking outside of any selectable object")]
|
||||
public bool keepSelection = true;
|
||||
|
||||
[NonSerialized] public Collider[] colliders;
|
||||
[NonSerialized] public Collider2D[] colliders2D;
|
||||
|
||||
public bool hasColliders => colliders != null && colliders.Length > 0;
|
||||
public bool hasColliders2D => colliders2D != null && colliders2D.Length > 0;
|
||||
|
||||
UnityEngine.Object currentCollider;
|
||||
static RaycastHit[] hits;
|
||||
static RaycastHit2D[] hits2D;
|
||||
static RaycastHit blockingHit;
|
||||
HighlightEffect hb;
|
||||
|
||||
public HighlightEffect highlightEffect { get { return hb; } }
|
||||
|
||||
public event OnObjectSelectionEvent OnObjectSelected;
|
||||
public event OnObjectSelectionEvent OnObjectUnSelected;
|
||||
public event OnObjectHighlightEvent OnObjectHighlightStart;
|
||||
public event OnObjectHighlightEvent OnObjectHighlightStay;
|
||||
public event OnObjectHighlightEvent OnObjectHighlightEnd;
|
||||
public event OnObjectClickEvent OnObjectClicked;
|
||||
|
||||
TriggerMode currentTriggerMode;
|
||||
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
static void DomainReloadDisabledSupport () {
|
||||
HighlightManager.selectedObjects.Clear();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
Init();
|
||||
}
|
||||
|
||||
private void OnValidate () {
|
||||
if (currentTriggerMode != triggerMode) {
|
||||
UpdateTriggers();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTriggers () {
|
||||
currentTriggerMode = triggerMode;
|
||||
if (currentTriggerMode == TriggerMode.RaycastOnThisObjectAndChildren) {
|
||||
colliders = GetComponentsInChildren<Collider>();
|
||||
colliders2D = GetComponentsInChildren<Collider2D>();
|
||||
if (hits == null || hits.Length != MAX_RAYCAST_HITS) {
|
||||
hits = new RaycastHit[MAX_RAYCAST_HITS];
|
||||
}
|
||||
if (hits2D == null || hits2D.Length != MAX_RAYCAST_HITS) {
|
||||
hits2D = new RaycastHit2D[MAX_RAYCAST_HITS];
|
||||
}
|
||||
if (Application.isPlaying) {
|
||||
StopAllCoroutines();
|
||||
if (isActiveAndEnabled) {
|
||||
StartCoroutine(DoRayCast());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Init () {
|
||||
if (raycastCamera == null) {
|
||||
raycastCamera = HighlightManager.GetCamera();
|
||||
}
|
||||
UpdateTriggers();
|
||||
if (hb == null) {
|
||||
hb = GetComponent<HighlightEffect>();
|
||||
}
|
||||
InputProxy.Init();
|
||||
}
|
||||
|
||||
void Start () {
|
||||
UpdateTriggers();
|
||||
if (triggerMode == TriggerMode.RaycastOnThisObjectAndChildren) {
|
||||
if (raycastCamera == null) {
|
||||
raycastCamera = HighlightManager.GetCamera();
|
||||
if (raycastCamera == null) {
|
||||
Debug.LogError("Highlight Trigger on " + gameObject.name + ": no camera found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!TryGetComponent(out Collider _)) {
|
||||
if (TryGetComponent(out MeshFilter _)) {
|
||||
gameObject.AddComponent<MeshCollider>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IEnumerator DoRayCast () {
|
||||
yield return null;
|
||||
WaitForEndOfFrame w = new WaitForEndOfFrame();
|
||||
while (triggerMode == TriggerMode.RaycastOnThisObjectAndChildren) {
|
||||
if (raycastCamera == null || !isActiveAndEnabled) {
|
||||
yield return null;
|
||||
continue;
|
||||
}
|
||||
|
||||
int hitCount;
|
||||
bool hit = false;
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
if (respectUI) {
|
||||
EventSystem es = EventSystem.current;
|
||||
if (es == null) {
|
||||
es = CreateEventSystem();
|
||||
}
|
||||
List<RaycastResult> raycastResults = new List<RaycastResult>();
|
||||
PointerEventData eventData = new PointerEventData(es);
|
||||
Vector3 cameraPos = raycastCamera.transform.position;
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
eventData.position = InputProxy.mousePosition;
|
||||
} else {
|
||||
eventData.position = new Vector2(raycastCamera.pixelWidth * 0.5f, raycastCamera.pixelHeight * 0.5f);
|
||||
}
|
||||
es.RaycastAll(eventData, raycastResults);
|
||||
hitCount = raycastResults.Count;
|
||||
// check UI blocker
|
||||
bool blocked = false;
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
RaycastResult rr = raycastResults[k];
|
||||
if (rr.module is UnityEngine.UI.GraphicRaycaster) {
|
||||
blocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blocked) {
|
||||
if (unhighlightOnUI && hb.highlighted) {
|
||||
hb.SetHighlighted(false);
|
||||
}
|
||||
yield return null;
|
||||
continue;
|
||||
}
|
||||
// look for our gameobject
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
RaycastResult rr = raycastResults[k];
|
||||
float distance = Vector3.Distance(rr.worldPosition, cameraPos);
|
||||
if (distance < minDistance || (maxDistance > 0 && distance > maxDistance)) continue;
|
||||
|
||||
GameObject theGameObject = rr.gameObject;
|
||||
for (int c = 0; c < colliders.Length; c++) {
|
||||
if (colliders[c].gameObject == theGameObject) {
|
||||
Collider theCollider = colliders[c];
|
||||
hit = true;
|
||||
UpdateHitPosition(theCollider.transform, rr.worldPosition);
|
||||
if (selectOnClick && InputProxy.GetMouseButtonDown(0)) {
|
||||
ToggleSelection();
|
||||
break;
|
||||
} else if (theCollider != currentCollider) {
|
||||
SwitchCollider(theCollider);
|
||||
k = hitCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if not blocked by UI and no hit found, fallback to raycast (required if no PhysicsRaycaster is present on the camera)
|
||||
|
||||
#endif
|
||||
Ray ray;
|
||||
{
|
||||
#if !(ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER)
|
||||
Vector2 screenPos = raycastSource == RayCastSource.MousePosition ? (Vector2)InputProxy.mousePosition : new Vector2(raycastCamera.pixelWidth * 0.5f, raycastCamera.pixelHeight * 0.5f);
|
||||
if (!CanInteractAt(screenPos)) {
|
||||
if (unhighlightOnUI && hb.highlighted) {
|
||||
hb.SetHighlighted(false);
|
||||
}
|
||||
yield return null;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
ray = raycastCamera.ScreenPointToRay(InputProxy.mousePosition);
|
||||
}
|
||||
else {
|
||||
ray = new Ray(raycastCamera.transform.position, raycastCamera.transform.forward);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VerifyHighlightStay();
|
||||
|
||||
bool isMouseButonDown = InputProxy.GetMouseButtonDown(0);
|
||||
float blockingDistance = GetBlockingDistance(ray, maxDistance > 0 ? maxDistance : float.MaxValue);
|
||||
if (hasColliders2D) {
|
||||
if (maxDistance > 0) {
|
||||
hitCount = Physics2D.GetRayIntersectionNonAlloc(ray, hits2D, maxDistance, raycastLayerMask);
|
||||
}
|
||||
else {
|
||||
hitCount = Physics2D.GetRayIntersectionNonAlloc(ray, hits2D, float.MaxValue, raycastLayerMask);
|
||||
}
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
float hitDistance = Vector3.Distance(hits2D[k].point, ray.origin);
|
||||
if (hitDistance < minDistance || hitDistance > blockingDistance) continue;
|
||||
Collider2D theCollider = hits2D[k].collider;
|
||||
int colliders2DCount = colliders2D.Length;
|
||||
for (int c = 0; c < colliders2DCount; c++) {
|
||||
if (colliders2D[c] == theCollider) {
|
||||
hit = true;
|
||||
UpdateHitPosition(hits2D[k].transform, hits2D[k].point);
|
||||
if (selectOnClick && isMouseButonDown) {
|
||||
ToggleSelection();
|
||||
break;
|
||||
}
|
||||
else if (theCollider != currentCollider) {
|
||||
SwitchCollider(theCollider);
|
||||
k = hitCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasColliders) {
|
||||
if (maxDistance > 0) {
|
||||
hitCount = Physics.RaycastNonAlloc(ray, hits, maxDistance, raycastLayerMask);
|
||||
}
|
||||
else {
|
||||
hitCount = Physics.RaycastNonAlloc(ray, hits, float.MaxValue, raycastLayerMask);
|
||||
}
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
float hitDistance = hits[k].distance;
|
||||
if (hitDistance < minDistance || hitDistance > blockingDistance) continue;
|
||||
Collider theCollider = hits[k].collider;
|
||||
int collidersCount = colliders.Length;
|
||||
for (int c = 0; c < collidersCount; c++) {
|
||||
if (colliders[c] == theCollider) {
|
||||
hit = true;
|
||||
UpdateHitPosition(hits[k].transform, hits[k].point);
|
||||
if (selectOnClick && isMouseButonDown) {
|
||||
ToggleSelection();
|
||||
break;
|
||||
}
|
||||
else if (theCollider != currentCollider) {
|
||||
SwitchCollider(theCollider);
|
||||
k = hitCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!hit && currentCollider != null) {
|
||||
SwitchCollider(null);
|
||||
}
|
||||
|
||||
if (selectOnClick && isMouseButonDown && !keepSelection && !hit) {
|
||||
yield return w; // wait for other potential triggers to act
|
||||
if (HighlightManager.lastTriggerFrame < Time.frameCount) {
|
||||
if (OnObjectUnSelected != null) OnObjectUnSelected(gameObject);
|
||||
HighlightManager.DeselectAll();
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
EventSystem CreateEventSystem() {
|
||||
GameObject eo = new GameObject("Event System created by Highlight Plus", typeof(EventSystem), typeof(UnityEngine.InputSystem.UI.InputSystemUIInputModule));
|
||||
return eo.GetComponent<EventSystem>();
|
||||
}
|
||||
#endif
|
||||
|
||||
void VerifyHighlightStay () {
|
||||
if (hb == null || !hb.highlighted) return;
|
||||
if (OnObjectHighlightStay != null && !OnObjectHighlightStay(hb.gameObject)) {
|
||||
SwitchCollider(null);
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchCollider (UnityEngine.Object newCollider) {
|
||||
if (!highlightOnHover && !hb.isSelected) return;
|
||||
|
||||
currentCollider = newCollider;
|
||||
if (currentCollider != null) {
|
||||
Highlight(true);
|
||||
}
|
||||
else {
|
||||
Highlight(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool CanInteract () {
|
||||
return CanInteractAt(InputProxy.mousePosition);
|
||||
}
|
||||
|
||||
bool CanInteractAt (Vector2 screenPosition) {
|
||||
if (!respectUI) return true;
|
||||
EventSystem es = EventSystem.current;
|
||||
if (es == null) return true;
|
||||
if (Application.isMobilePlatform && InputProxy.touchCount > 0) {
|
||||
if (es.IsPointerOverGameObject(InputProxy.GetFingerIdFromTouch(0))) {
|
||||
return false;
|
||||
}
|
||||
if (raycastSource == RayCastSource.CameraDirection) {
|
||||
return !IsUIAtScreenPosition(es, screenPosition);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (raycastSource == RayCastSource.MousePosition) {
|
||||
if (es.IsPointerOverGameObject(-1)) return false;
|
||||
return true;
|
||||
}
|
||||
return !IsUIAtScreenPosition(es, screenPosition);
|
||||
}
|
||||
|
||||
bool IsUIAtScreenPosition (EventSystem es, Vector2 screenPosition) {
|
||||
List<RaycastResult> raycastResults = new List<RaycastResult>();
|
||||
PointerEventData eventData = new PointerEventData(es);
|
||||
eventData.position = screenPosition;
|
||||
es.RaycastAll(eventData, raycastResults);
|
||||
int hitCount = raycastResults.Count;
|
||||
for (int k = 0; k < hitCount; k++) {
|
||||
RaycastResult rr = raycastResults[k];
|
||||
if (rr.module is UnityEngine.UI.GraphicRaycaster) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float GetBlockingDistance (Ray ray, float maxRayDistance) {
|
||||
if (blockingLayerMask == 0) return float.MaxValue;
|
||||
if (hasColliders2D) {
|
||||
RaycastHit2D hit2D = Physics2D.Raycast(ray.origin, ray.direction, maxRayDistance, blockingLayerMask);
|
||||
if (hit2D.collider != null) {
|
||||
return hit2D.distance + 0.01f;
|
||||
}
|
||||
}
|
||||
if (Physics.Raycast(ray, out blockingHit, maxRayDistance, blockingLayerMask)) {
|
||||
return blockingHit.distance + 0.01f;
|
||||
}
|
||||
return float.MaxValue;
|
||||
}
|
||||
|
||||
|
||||
void OnMouseDown () {
|
||||
if (isActiveAndEnabled && triggerMode == TriggerMode.ColliderEventsOnlyOnThisObject) {
|
||||
if (!CanInteract()) return;
|
||||
if (selectOnClick && InputProxy.GetMouseButtonDown(0)) {
|
||||
ToggleSelection();
|
||||
return;
|
||||
}
|
||||
Highlight(true);
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseEnter () {
|
||||
if (isActiveAndEnabled && triggerMode == TriggerMode.ColliderEventsOnlyOnThisObject) {
|
||||
if (!CanInteract()) return;
|
||||
Highlight(true);
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseExit () {
|
||||
if (isActiveAndEnabled && triggerMode == TriggerMode.ColliderEventsOnlyOnThisObject) {
|
||||
if (!CanInteract()) return;
|
||||
Highlight(false);
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseOver () {
|
||||
if (hb == null || !hb.labelEnabled) return;
|
||||
Ray ray = raycastCamera.ScreenPointToRay(InputProxy.mousePosition);
|
||||
if (Physics.Raycast(ray, out RaycastHit hit)) {
|
||||
UpdateHitPosition(hit.transform, hit.point);
|
||||
}
|
||||
else {
|
||||
UpdateHitPosition(transform, transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
void Highlight (bool state) {
|
||||
if (state) {
|
||||
if (!hb.highlighted) {
|
||||
if (OnObjectHighlightStart != null && hb.target != null) {
|
||||
if (!OnObjectHighlightStart(hb.target.gameObject)) {
|
||||
currentCollider = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hb.highlighted) {
|
||||
if (OnObjectHighlightEnd != null && hb.target != null) {
|
||||
OnObjectHighlightEnd(hb.target.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectOnClick || hb.isSelected) {
|
||||
if (hb.isSelected) {
|
||||
if (state && selectedAndHighlightedProfile != null) {
|
||||
selectedAndHighlightedProfile.Load(hb);
|
||||
}
|
||||
else if (selectedProfile != null) {
|
||||
selectedProfile.Load(hb);
|
||||
}
|
||||
else if (hb.previousSettings != null) {
|
||||
hb.previousSettings.Load(hb);
|
||||
}
|
||||
if (hb.highlighted) {
|
||||
hb.UpdateMaterialProperties();
|
||||
}
|
||||
else {
|
||||
hb.SetHighlighted(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (!highlightOnHover) {
|
||||
hb.SetHighlighted(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
hb.SetHighlighted(state);
|
||||
}
|
||||
|
||||
void UpdateHitPosition (Transform target, Vector3 positionWS = default, Vector3 normalWS = default) {
|
||||
if (hb == null) return;
|
||||
Vector3 localPosition = target.InverseTransformPoint(positionWS);
|
||||
hb.SetHitPosition(target, localPosition, Misc.vector3Zero, normalWS);
|
||||
|
||||
if (InputProxy.GetMouseButtonDown(0) && OnObjectClicked != null) {
|
||||
OnObjectClicked(target.gameObject, positionWS, normalWS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ToggleSelection () {
|
||||
|
||||
HighlightManager.lastTriggerFrame = Time.frameCount;
|
||||
|
||||
bool newState = toggle ? !hb.isSelected : true;
|
||||
if (newState) {
|
||||
if (OnObjectSelected != null && !OnObjectSelected(gameObject)) return;
|
||||
}
|
||||
else {
|
||||
if (OnObjectUnSelected != null && !OnObjectUnSelected(gameObject)) return;
|
||||
}
|
||||
|
||||
if (singleSelection && newState) {
|
||||
HighlightManager.DeselectAll();
|
||||
}
|
||||
hb.isSelected = newState;
|
||||
if (newState && !HighlightManager.selectedObjects.Contains(hb)) {
|
||||
HighlightManager.selectedObjects.Add(hb);
|
||||
}
|
||||
else if (!newState && HighlightManager.selectedObjects.Contains(hb)) {
|
||||
HighlightManager.selectedObjects.Remove(hb);
|
||||
}
|
||||
|
||||
if (hb.isSelected) {
|
||||
if (hb.previousSettings == null) {
|
||||
hb.previousSettings = ScriptableObject.CreateInstance<HighlightProfile>();
|
||||
}
|
||||
hb.previousSettings.Save(hb);
|
||||
}
|
||||
else {
|
||||
hb.RestorePreviousHighlightEffectSettings();
|
||||
}
|
||||
|
||||
Highlight(true);
|
||||
}
|
||||
|
||||
public void OnTriggerEnter (Collider other) {
|
||||
if (triggerMode == TriggerMode.Volume) {
|
||||
if ((volumeLayerMask & (1 << other.gameObject.layer)) != 0) {
|
||||
Highlight(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTriggerExit (Collider other) {
|
||||
if (triggerMode == TriggerMode.Volume) {
|
||||
if ((volumeLayerMask & (1 << other.gameObject.layer)) != 0) {
|
||||
Highlight(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5009cbb7e54994bb586cde7a70f34e6b
|
||||
timeCreated: 1542876337
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
90
Assets/HighlightPlus/Runtime/Scripts/InputProxy.cs
Normal file
90
Assets/HighlightPlus/Runtime/Scripts/InputProxy.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using UnityEngine;
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using EnhancedTouch = UnityEngine.InputSystem.EnhancedTouch;
|
||||
#endif
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public static class InputProxy {
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
|
||||
static Vector3 lastPointerPosition;
|
||||
|
||||
public static void Init() {
|
||||
if (!EnhancedTouch.EnhancedTouchSupport.enabled) {
|
||||
EnhancedTouch.EnhancedTouchSupport.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3 mousePosition {
|
||||
get {
|
||||
if (touchCount > 0) {
|
||||
lastPointerPosition = EnhancedTouch.Touch.activeTouches[0].screenPosition;
|
||||
} else {
|
||||
Mouse m = Mouse.current;
|
||||
if (m != null) {
|
||||
lastPointerPosition = m.position.ReadValue();
|
||||
}
|
||||
}
|
||||
return lastPointerPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetMouseButtonDown(int buttonIndex) {
|
||||
if (touchCount > 0) {
|
||||
return EnhancedTouch.Touch.activeTouches[0].phase == UnityEngine.InputSystem.TouchPhase.Began;
|
||||
} else {
|
||||
Mouse m = Mouse.current;
|
||||
if (m == null) return false;
|
||||
switch (buttonIndex) {
|
||||
case 1: return m.rightButton.wasPressedThisFrame;
|
||||
case 2: return m.middleButton.wasPressedThisFrame;
|
||||
default: return m.leftButton.wasPressedThisFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int touchCount { get { return EnhancedTouch.Touch.activeTouches.Count; } }
|
||||
|
||||
public static int GetFingerIdFromTouch(int touchIndex) {
|
||||
EnhancedTouch.Touch touch = EnhancedTouch.Touch.activeTouches[touchIndex];
|
||||
return touch.finger.index;
|
||||
}
|
||||
|
||||
public static bool GetKeyDown(string name) {
|
||||
return ((KeyControl)Keyboard.current[name]).wasPressedThisFrame;
|
||||
}
|
||||
|
||||
#else
|
||||
public static void Init() {}
|
||||
|
||||
public static Vector3 mousePosition {
|
||||
get {
|
||||
return Input.mousePosition;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetMouseButtonDown(int buttonIndex) {
|
||||
return Input.GetMouseButtonDown(buttonIndex);
|
||||
}
|
||||
|
||||
public static int touchCount {
|
||||
get { return Input.touchCount; }
|
||||
}
|
||||
|
||||
public static int GetFingerIdFromTouch(int touchIndex) {
|
||||
return Input.GetTouch(touchIndex).fingerId;
|
||||
}
|
||||
|
||||
public static bool GetKeyDown(string name) {
|
||||
return Input.GetKeyDown(name);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/InputProxy.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/InputProxy.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22dd181849f53482686b5c26304410c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
41
Assets/HighlightPlus/Runtime/Scripts/Misc.cs
Normal file
41
Assets/HighlightPlus/Runtime/Scripts/Misc.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public class Misc {
|
||||
|
||||
public static Vector2 vector2Zero = new Vector2(0, 0);
|
||||
public static Vector2 vector2One = new Vector2(1, 1);
|
||||
public static Vector3 vector3Zero = new Vector3(0, 0, 0);
|
||||
public static Vector3 vector3Max = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
|
||||
public static Vector3 vector3Min = new Vector3(float.MinValue, float.MinValue, float.MinValue);
|
||||
|
||||
public static T FindObjectOfType<T>(bool includeInactive = false) where T : UnityEngine.Object {
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
return UnityEngine.Object.FindAnyObjectByType<T>(includeInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude);
|
||||
#else
|
||||
return UnityEngine.Object.FindObjectOfType<T>(includeInactive);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static UnityEngine.Object[] FindObjectsOfType(Type type, bool includeInactive = false) {
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
return UnityEngine.Object.FindObjectsByType(type, includeInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude, FindObjectsSortMode.None);
|
||||
#else
|
||||
return UnityEngine.Object.FindObjectsOfType(type, includeInactive);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static T[] FindObjectsOfType<T>(bool includeInactive = false) where T : UnityEngine.Object {
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
return UnityEngine.Object.FindObjectsByType<T>(includeInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude, FindObjectsSortMode.None);
|
||||
#else
|
||||
return UnityEngine.Object.FindObjectsOfType<T>(includeInactive);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/Misc.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/Misc.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a87ac1812c2d44355a6c2acddcdd42ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/HighlightPlus/Runtime/Scripts/RenderingUtils.cs
Normal file
47
Assets/HighlightPlus/Runtime/Scripts/RenderingUtils.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public static class RenderingUtils {
|
||||
|
||||
static Mesh _fullScreenMesh;
|
||||
static Mesh fullscreenMesh {
|
||||
get {
|
||||
if (_fullScreenMesh != null) {
|
||||
return _fullScreenMesh;
|
||||
}
|
||||
float num = 1f;
|
||||
float num2 = 0f;
|
||||
Mesh val = new Mesh();
|
||||
_fullScreenMesh = val;
|
||||
_fullScreenMesh.SetVertices(new List<Vector3> {
|
||||
new Vector3 (-1f, -1f, 0f),
|
||||
new Vector3 (-1f, 1f, 0f),
|
||||
new Vector3 (1f, -1f, 0f),
|
||||
new Vector3 (1f, 1f, 0f)
|
||||
});
|
||||
_fullScreenMesh.SetUVs(0, new List<Vector2> {
|
||||
new Vector2 (0f, num2),
|
||||
new Vector2 (0f, num),
|
||||
new Vector2 (1f, num2),
|
||||
new Vector2 (1f, num)
|
||||
});
|
||||
_fullScreenMesh.SetIndices(new int[6] { 0, 1, 2, 2, 1, 3 }, (MeshTopology)0, 0, false);
|
||||
_fullScreenMesh.UploadMeshData(true);
|
||||
return _fullScreenMesh;
|
||||
}
|
||||
}
|
||||
|
||||
static Matrix4x4 matrix4x4Identity = Matrix4x4.identity;
|
||||
public static void FullScreenBlit(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int passIndex) {
|
||||
destination = new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1);
|
||||
cmd.SetRenderTarget(destination);
|
||||
cmd.SetGlobalTexture(ShaderParams.MainTex, source);
|
||||
cmd.DrawMesh(fullscreenMesh, matrix4x4Identity, material, 0, passIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/RenderingUtils.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/RenderingUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95fcf366e400b4f79938bee11ff1ddc0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
147
Assets/HighlightPlus/Runtime/Scripts/ShaderParams.cs
Normal file
147
Assets/HighlightPlus/Runtime/Scripts/ShaderParams.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
public static class ShaderParams {
|
||||
|
||||
// general uniforms
|
||||
public static int Cull = Shader.PropertyToID("_Cull");
|
||||
public static int BlurScale = Shader.PropertyToID("_BlurScale");
|
||||
public static int BlurScaleFirstHoriz = Shader.PropertyToID("_BlurScaleFirstHoriz");
|
||||
public static int Speed = Shader.PropertyToID("_Speed");
|
||||
public static int ConstantWidth = Shader.PropertyToID("_ConstantWidth");
|
||||
public static int MinimumWidth = Shader.PropertyToID("_MinimumWidth");
|
||||
public static int CutOff = Shader.PropertyToID("_CutOff");
|
||||
public static int ZTest = Shader.PropertyToID("_ZTest");
|
||||
public static int Flip = Shader.PropertyToID("_Flip");
|
||||
public static int Debug = Shader.PropertyToID("_Debug");
|
||||
public static int Color = Shader.PropertyToID("_Color");
|
||||
public static int MainTex = Shader.PropertyToID("_MainTex");
|
||||
public static int BaseMap = Shader.PropertyToID("_BaseMap");
|
||||
public static int BaseMapST = Shader.PropertyToID("_BaseMap_ST");
|
||||
public static int BlendSrc = Shader.PropertyToID("_BlendSrc");
|
||||
public static int BlendDst = Shader.PropertyToID("_BlendDst");
|
||||
public static int FadeFactor = Shader.PropertyToID("_HP_Fade");
|
||||
public static int Padding = Shader.PropertyToID("_Padding");
|
||||
public static int ResampleScale = Shader.PropertyToID("_ResampleScale");
|
||||
public static int NoiseTex = Shader.PropertyToID("_NoiseTex");
|
||||
public static int Pixelation = Shader.PropertyToID("_Pixelation");
|
||||
|
||||
// outline uniforms
|
||||
public static int OutlineWidth = Shader.PropertyToID("_OutlineWidth");
|
||||
public static int OutlineZTest = Shader.PropertyToID("_OutlineZTest");
|
||||
public static int OutlineDirection = Shader.PropertyToID("_OutlineDirection");
|
||||
public static int OutlineColor = Shader.PropertyToID("_OutlineColor");
|
||||
public static int OutlineVertexWidth = Shader.PropertyToID("_OutlineVertexWidth");
|
||||
public static int OutlineGradientTex = Shader.PropertyToID("_OutlineGradientTex");
|
||||
public static int OutlineVertexData = Shader.PropertyToID("_OutlineVertexData");
|
||||
public static int OutlineStencilComp = Shader.PropertyToID("_OutlineStencilComp");
|
||||
public static int OutlineEdgeThreshold = Shader.PropertyToID("_EdgeThreshold");
|
||||
public static int OutlineSharpness = Shader.PropertyToID("_OutlineSharpness");
|
||||
|
||||
// glow uniforms
|
||||
public static int GlowZTest = Shader.PropertyToID("_GlowZTest");
|
||||
public static int GlowStencilComp = Shader.PropertyToID("_GlowStencilComp");
|
||||
public static int GlowStencilOp = Shader.PropertyToID("_GlowStencilOp");
|
||||
public static int GlowDirection = Shader.PropertyToID("_GlowDirection");
|
||||
public static int Glow = Shader.PropertyToID("_Glow");
|
||||
public static int GlowColor = Shader.PropertyToID("_GlowColor");
|
||||
public static int Glow2 = Shader.PropertyToID("_Glow2");
|
||||
|
||||
// see-through uniforms
|
||||
public static int SeeThrough = Shader.PropertyToID("_SeeThrough");
|
||||
public static int SeeThroughNoise = Shader.PropertyToID("_SeeThroughNoise");
|
||||
public static int SeeThroughBorderWidth = Shader.PropertyToID("_SeeThroughBorderWidth");
|
||||
public static int SeeThroughBorderConstantWidth = Shader.PropertyToID("_SeeThroughBorderConstantWidth");
|
||||
public static int SeeThroughTintColor = Shader.PropertyToID("_SeeThroughTintColor");
|
||||
public static int SeeThroughBorderColor = Shader.PropertyToID("_SeeThroughBorderColor");
|
||||
public static int SeeThroughStencilRef = Shader.PropertyToID("_SeeThroughStencilRef");
|
||||
public static int SeeThroughStencilComp = Shader.PropertyToID("_SeeThroughStencilComp");
|
||||
public static int SeeThroughStencilPassOp = Shader.PropertyToID("_SeeThroughStencilPassOp");
|
||||
public static int SeeThroughDepthOffset = Shader.PropertyToID("_SeeThroughDepthOffset");
|
||||
public static int SeeThroughMaxDepth = Shader.PropertyToID("_SeeThroughMaxDepth");
|
||||
public static int SeeThroughFadeRange = Shader.PropertyToID("_SeeThroughFadeRange");
|
||||
public static int SeeThroughTexture = Shader.PropertyToID("_SeeThroughTexture");
|
||||
public static int SeeThroughTextureScale = Shader.PropertyToID("_SeeThroughTextureScale");
|
||||
|
||||
// inner glow uniforms
|
||||
public static int InnerGlowData = Shader.PropertyToID("_InnerGlowData");
|
||||
public static int InnerGlowZTest = Shader.PropertyToID("_InnerGlowZTest");
|
||||
public static int InnerGlowColor = Shader.PropertyToID("_InnerGlowColor");
|
||||
public static int InnerGlowBlendMode = Shader.PropertyToID("_InnerGlowBlendMode");
|
||||
|
||||
// mask uniforms (not declared in Properties so globals take effect over material defaults)
|
||||
public static int MaskStencilRef = Shader.PropertyToID("_MaskStencilRef");
|
||||
public static int MaskWriteMask = Shader.PropertyToID("_MaskWriteMask");
|
||||
|
||||
// focus uniforms
|
||||
public static int FocusColor = Shader.PropertyToID("_FocusColor");
|
||||
public static int FocusBlurTex = Shader.PropertyToID("_FocusBlurTex");
|
||||
public static int FocusBlurRT = Shader.PropertyToID("_HPFocusBlurRT");
|
||||
public static int FocusBlurTempRT = Shader.PropertyToID("_HPFocusBlurTempRT");
|
||||
public static int FocusDesaturation = Shader.PropertyToID("_FocusDesaturation");
|
||||
|
||||
// overlay uniforms
|
||||
public static int OverlayData = Shader.PropertyToID("_OverlayData");
|
||||
public static int OverlayBackColor = Shader.PropertyToID("_OverlayBackColor");
|
||||
public static int OverlayColor = Shader.PropertyToID("_OverlayColor");
|
||||
public static int OverlayHitPosData = Shader.PropertyToID("_OverlayHitPosData");
|
||||
public static int OverlayHitStartTime = Shader.PropertyToID("_OverlayHitStartTime");
|
||||
public static int OverlayTexture = Shader.PropertyToID("_OverlayTexture");
|
||||
public static int OverlayTextureScrolling = Shader.PropertyToID("_OverlayTextureScrolling");
|
||||
public static int OverlayZTest = Shader.PropertyToID("_OverlayZTest");
|
||||
public static int OverlayPatternScrolling = Shader.PropertyToID("_OverlayPatternScrolling");
|
||||
public static int OverlayPatternData = Shader.PropertyToID("_OverlayPatternData");
|
||||
public static int OverlayBlendDst = Shader.PropertyToID("_OverlayBlendDst");
|
||||
|
||||
// target uniforms
|
||||
public static int TargetFXRenderData = Shader.PropertyToID("_TargetFXRenderData");
|
||||
public static int TargetFXFrameData = Shader.PropertyToID("_TargetFXFrameData");
|
||||
public static int GlowRT = Shader.PropertyToID("_HPComposeGlowFinal");
|
||||
public static int OutlineRT = Shader.PropertyToID("_HPComposeOutlineFinal");
|
||||
|
||||
// icon uniforms
|
||||
public static int IconFXDarkColor = Shader.PropertyToID("_DarkColor");
|
||||
|
||||
// custom vertex transform
|
||||
public static int VertexTransformMode = Shader.PropertyToID("_HP_VertexTransformMode");
|
||||
|
||||
// pattern uniforms
|
||||
public static int PatternTex = Shader.PropertyToID("_PatternTex");
|
||||
public static int DistortionTex = Shader.PropertyToID("_DistortionTex");
|
||||
public static int PatternData = Shader.PropertyToID("_PatternData");
|
||||
|
||||
// dashed outline
|
||||
public static int DashData = Shader.PropertyToID("_DashData");
|
||||
|
||||
// outline gradient
|
||||
public static int OutlineGradientData = Shader.PropertyToID("_OutlineGradientData");
|
||||
|
||||
// keywords
|
||||
public const string SKW_ALPHACLIP = "HP_ALPHACLIP";
|
||||
public const string SKW_OUTLINE_GRADIENT_WS = "HP_OUTLINE_GRADIENT_WS";
|
||||
public const string SKW_OUTLINE_GRADIENT_LS = "HP_OUTLINE_GRADIENT_LS";
|
||||
public const string SKW_ALL_EDGES = "HP_ALL_EDGES";
|
||||
public const string SKW_DEPTHCLIP = "HP_DEPTHCLIP";
|
||||
public const string SKW_DEPTHCLIP_INV = "HP_DEPTHCLIP_INV";
|
||||
public const string SKW_DEPTH_OFFSET = "HP_DEPTH_OFFSET";
|
||||
public const string SKW_TEXTURE_TRIPLANAR = "HP_TEXTURE_TRIPLANAR";
|
||||
public const string SKW_TEXTURE_TRIPLANAR_LOCAL = "HP_TEXTURE_TRIPLANAR_LOCAL";
|
||||
public const string SKW_TEXTURE_SCREENSPACE = "HP_TEXTURE_SCREENSPACE";
|
||||
public const string SKW_TEXTURE_OBJECTSPACE = "HP_TEXTURE_OBJECTSPACE";
|
||||
public const string SKW_SEETHROUGH_ONLY_BORDER = "HP_SEETHROUGH_ONLY_BORDER";
|
||||
public const string SKW_MASK_CUTOUT = "HP_MASK_CUTOUT";
|
||||
public const string SKW_DITHER_BLUENOISE = "HP_DITHER_BLUENOISE";
|
||||
public const string SKW_OUTLINE_STYLIZED = "HP_STYLIZED";
|
||||
public const string SKW_OUTLINE_DASHED = "HP_DASHED";
|
||||
public const string SKW_TARGET_FRAME = "HP_TARGET_FRAME";
|
||||
public const string SKW_TARGET_INWARD_CORNERS = "HP_TARGET_INWARD_CORNERS";
|
||||
public const string SKW_TARGET_CROSS = "HP_TARGET_CROSS";
|
||||
public const string SKW_PATTERN_POLKADOTS = "HP_PATTERN_POLKADOTS";
|
||||
public const string SKW_PATTERN_GRID = "HP_PATTERN_GRID";
|
||||
public const string SKW_PATTERN_STAGGERED_LINES = "HP_PATTERN_STAGGERED_LINES";
|
||||
public const string SKW_PATTERN_ZIGZAG = "HP_PATTERN_ZIGZAG";
|
||||
public const string SKW_SOURCE_SOLID_COLOR = "SOURCE_SOLID_COLOR";
|
||||
}
|
||||
}
|
||||
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/ShaderParams.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/ShaderParams.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c43adc34509547febe8181a2de00da5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
62
Assets/HighlightPlus/Runtime/Scripts/VRCheck.cs
Normal file
62
Assets/HighlightPlus/Runtime/Scripts/VRCheck.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// Comment this line to disable usage of XR module
|
||||
#define ENABLE_XR
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#if ENABLE_XR
|
||||
using UnityEngine.XR;
|
||||
#endif
|
||||
|
||||
namespace HighlightPlus {
|
||||
|
||||
static class VRCheck {
|
||||
|
||||
public static bool isActive;
|
||||
public static bool isVrRunning;
|
||||
|
||||
#if !ENABLE_XR
|
||||
static bool IsActive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsVrRunning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static readonly List<XRDisplaySubsystemDescriptor> displaysDescs = new List<XRDisplaySubsystemDescriptor>();
|
||||
static readonly List<XRDisplaySubsystem> displays = new List<XRDisplaySubsystem>();
|
||||
|
||||
static bool IsActive() {
|
||||
displaysDescs.Clear();
|
||||
SubsystemManager.GetSubsystemDescriptors(displaysDescs);
|
||||
|
||||
// If there are registered display descriptors that is a good indication that VR is most likely "enabled"
|
||||
return displaysDescs.Count > 0;
|
||||
}
|
||||
|
||||
static bool IsVrRunning() {
|
||||
bool vrIsRunning = false;
|
||||
displays.Clear();
|
||||
SubsystemManager.GetSubsystems(displays);
|
||||
foreach (var displaySubsystem in displays) {
|
||||
if (displaySubsystem.running) {
|
||||
vrIsRunning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return vrIsRunning;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static void Init() {
|
||||
isActive = IsActive();
|
||||
isVrRunning = IsVrRunning();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/HighlightPlus/Runtime/Scripts/VRCheck.cs.meta
Normal file
11
Assets/HighlightPlus/Runtime/Scripts/VRCheck.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5a7be65460ba4182b08a8f602571e25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user