189 lines
6.4 KiB
C#
189 lines
6.4 KiB
C#
/* SimpleLOD 1.5a */
|
|
/* By Orbcreation BV */
|
|
/* Richard Knol */
|
|
/* March 18, 2015 */
|
|
|
|
using UnityEngine;
|
|
using System;
|
|
using System.Collections;
|
|
using OrbCreationExtensions;
|
|
|
|
public class LODSwitcher : MonoBehaviour {
|
|
|
|
public Mesh[] lodMeshes;
|
|
public GameObject[] lodGameObjects;
|
|
public float[] lodScreenSizes;
|
|
public float deactivateAtDistance = 0f;
|
|
public Camera customCamera = null;
|
|
|
|
private MeshFilter meshFilter;
|
|
private SkinnedMeshRenderer skinnedMeshRenderer;
|
|
private Vector3 centerOffset;
|
|
private float pixelsPerMeter;
|
|
private float objectSize;
|
|
private int fixedLODLevel = -1;
|
|
private int lodLevel = 0;
|
|
private int frameOffset = 0;
|
|
private int frameInterval = 10;
|
|
|
|
|
|
void Start() {
|
|
frameOffset = Mathf.RoundToInt(UnityEngine.Random.value * 10f);
|
|
if((lodMeshes == null || lodMeshes.Length <= 0) && (lodGameObjects == null || lodGameObjects.Length <= 0)) {
|
|
Debug.LogWarning(gameObject.name+".LODSwitcher: No lodMeshes/lodGameObjects set. LODSwitcher is now disabled.");
|
|
enabled = false;
|
|
}
|
|
int nrOfLevels = 0;
|
|
if(lodMeshes != null) nrOfLevels = lodMeshes.Length-1;
|
|
if(lodGameObjects != null) nrOfLevels = Mathf.Max(nrOfLevels, lodGameObjects.Length-1);
|
|
if(enabled && (lodScreenSizes == null || lodScreenSizes.Length != nrOfLevels)) {
|
|
Debug.LogWarning(gameObject.name+".LODSwitcher: lodScreenSizes should have a length of " + nrOfLevels + ". LODSwitcher is now disabled.");
|
|
enabled = false;
|
|
}
|
|
SetLODLevel(0);
|
|
ComputeDimensions();
|
|
lodLevel = -1;
|
|
ComputeLODLevel();
|
|
}
|
|
|
|
public void ComputeDimensions() {
|
|
Bounds bounds = gameObject.GetWorldBounds();
|
|
centerOffset = bounds.center - transform.position;
|
|
objectSize = bounds.size.magnitude;
|
|
if(skinnedMeshRenderer == null && meshFilter == null) GetMeshFilter();
|
|
if(skinnedMeshRenderer != null) {
|
|
bounds = skinnedMeshRenderer.localBounds;
|
|
objectSize = bounds.size.magnitude;
|
|
centerOffset = bounds.center;
|
|
frameInterval = 1;
|
|
}
|
|
Camera cam = customCamera;
|
|
if(cam == null) cam = Camera.main;
|
|
if(cam == null) {
|
|
Debug.LogWarning("No scene camera found yet, you need to call LODSwitcher.ComputeDimensions() again when you have your Camera set up");
|
|
return;
|
|
}
|
|
Vector3 p0 = cam.ScreenToWorldPoint(new Vector3((Screen.width - 100f) / 2f, 0, 1f));
|
|
Vector3 p1 = cam.ScreenToWorldPoint(new Vector3((Screen.width + 100f) / 2f, 0, 1f));
|
|
pixelsPerMeter = 1f / (Vector3.Distance(p0, p1) / 100f);
|
|
}
|
|
public void SetCustomCamera(Camera aCamera) {
|
|
customCamera = aCamera;
|
|
ComputeDimensions();
|
|
}
|
|
|
|
public void SetFixedLODLevel(int aLevel) {
|
|
fixedLODLevel = Mathf.Max(0, Mathf.Min(MaxLODLevel(), aLevel));
|
|
}
|
|
public void ReleaseFixedLODLevel() {
|
|
fixedLODLevel = -1;
|
|
}
|
|
|
|
public int GetLODLevel() {
|
|
return lodLevel;
|
|
}
|
|
|
|
public int MaxLODLevel() {
|
|
if(lodScreenSizes == null) return 0;
|
|
return lodScreenSizes.Length;
|
|
}
|
|
|
|
public Mesh GetMesh(int aLevel) {
|
|
if(lodMeshes != null && lodMeshes.Length >= aLevel) return lodMeshes[aLevel];
|
|
return null;
|
|
}
|
|
|
|
public void SetMesh(Mesh aMesh, int aLevel) {
|
|
if(lodMeshes == null) lodMeshes = new Mesh[aLevel+1];
|
|
if(lodMeshes.Length <= aLevel) Array.Resize(ref lodMeshes, aLevel + 1);
|
|
if(aLevel > 0) {
|
|
if(lodScreenSizes == null) lodScreenSizes = new float[aLevel];
|
|
if(lodScreenSizes.Length < aLevel) Array.Resize(ref lodScreenSizes, aLevel);
|
|
}
|
|
lodMeshes[aLevel] = aMesh;
|
|
if(aLevel == lodLevel) {
|
|
lodLevel = -1;
|
|
SetLODLevel(aLevel); // ensure we use the new model
|
|
}
|
|
ComputeDimensions();
|
|
}
|
|
|
|
public void SetRelativeScreenSize(float aValue, int aLevel) {
|
|
if(lodScreenSizes == null) lodScreenSizes = new float[aLevel];
|
|
if(lodScreenSizes.Length < aLevel) Array.Resize(ref lodScreenSizes, aLevel);
|
|
for(int i=0;i<lodScreenSizes.Length;i++) {
|
|
if(i + 1 == aLevel) lodScreenSizes[i] = aValue;
|
|
else if(lodScreenSizes[i] == 0f) {
|
|
if(i == 0) lodScreenSizes[i] = 0.6f;
|
|
else lodScreenSizes[i] = lodScreenSizes[i - 1] * 0.5f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Update () {
|
|
if((Time.frameCount + frameOffset) % frameInterval != 0) return; // no need to do this every frame for every object in the scene
|
|
ComputeLODLevel();
|
|
}
|
|
|
|
public Vector3 NearestCameraPositionForLOD(int aLevel) {
|
|
ComputeDimensions();
|
|
Camera cam = customCamera;
|
|
if(cam == null) cam = Camera.main;
|
|
if(aLevel > 0 && aLevel <= lodScreenSizes.Length) {
|
|
float pixelSize = objectSize * pixelsPerMeter;
|
|
float distance = pixelSize / Screen.width / lodScreenSizes[aLevel-1];
|
|
return transform.position + centerOffset + (cam.transform.rotation * (Vector3.back * distance));
|
|
}
|
|
return cam.transform.position;
|
|
}
|
|
|
|
|
|
public float ScreenPortion() {
|
|
Camera cam = customCamera;
|
|
if(cam == null) cam = Camera.main;
|
|
float distance = Vector3.Distance(cam.transform.position, transform.position + centerOffset);
|
|
if(deactivateAtDistance > 0f && distance > deactivateAtDistance) return -1f;
|
|
float pixelSize = objectSize * pixelsPerMeter;
|
|
float screenPortion = pixelSize / distance / Screen.width;
|
|
return Mathf.RoundToInt(screenPortion * 40f) * 0.025f; // round to steps of 0.025 to prevent frequent switching back and forth
|
|
}
|
|
|
|
private void ComputeLODLevel() {
|
|
int newLodLevel = 0;
|
|
if(fixedLODLevel >= 0) {
|
|
newLodLevel = fixedLODLevel;
|
|
} else {
|
|
float screenPortion = ScreenPortion();
|
|
if(screenPortion >= 0f) {
|
|
for(int i=0;i<lodScreenSizes.Length;i++) {
|
|
if(screenPortion < lodScreenSizes[i]) newLodLevel++;
|
|
}
|
|
} else newLodLevel = -1;
|
|
}
|
|
if(newLodLevel != lodLevel) SetLODLevel(newLodLevel);
|
|
}
|
|
|
|
private void GetMeshFilter() {
|
|
skinnedMeshRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
|
|
if(skinnedMeshRenderer == null) meshFilter = gameObject.GetComponent<MeshFilter>();
|
|
}
|
|
|
|
public void SetLODLevel(int newLodLevel) {
|
|
if(newLodLevel != lodLevel) {
|
|
newLodLevel = Mathf.Min(MaxLODLevel(), newLodLevel);
|
|
if(newLodLevel < 0) {
|
|
gameObject.GetComponent<Renderer>().enabled = false;
|
|
} else {
|
|
if(lodMeshes != null && lodMeshes.Length > 0) gameObject.GetComponent<Renderer>().enabled = true;
|
|
if(lodMeshes != null && lodMeshes.Length > newLodLevel) {
|
|
if(skinnedMeshRenderer == null && meshFilter == null) GetMeshFilter();
|
|
if(skinnedMeshRenderer != null) skinnedMeshRenderer.sharedMesh = lodMeshes[newLodLevel];
|
|
else if(meshFilter != null) meshFilter.sharedMesh = lodMeshes[newLodLevel];
|
|
}
|
|
for(int i=0;lodGameObjects!=null && i<lodGameObjects.Length;i++) lodGameObjects[i].SetActive(i==newLodLevel);
|
|
}
|
|
lodLevel = newLodLevel;
|
|
}
|
|
}
|
|
}
|