Files
kridoo 6e91a0c7f0 111
2025-09-15 17:32:08 +08:00

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;
}
}
}