Files
Project-M/Packages/com.rukhanka.animation/Rukhanka.Runtime/Culling/AnimationCullingContextUpdateSystem.cs
T
2026-05-31 14:27:52 -07:00

171 lines
5.8 KiB
C#

#if !RUKHANKA_NO_DEBUG_DRAWER
using Rukhanka.DebugDrawer;
#endif
using Rukhanka.Toolbox;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
#if RUKHANKA_WITH_NETCODE
using Unity.NetCode;
#endif
using UnityEditor;
using UnityEngine;
/////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
[DisableAutoCreation]
[UpdateAfter(typeof(FillAnimationsFromControllerSystem))]
public partial class AnimationCullingContextUpdateSystem: SystemBase
{
internal struct CullingCameraData
{
public float3 pos;
public float3 viewDir;
public float4x4 cullingMatrix;
public float fieldOfView;
public bool isOrthographic;
public float orthographicSize;
}
internal struct LightData
{
public float4 posOrDir;
public float range;
}
/////////////////////////////////////////////////////////////////////////////////
protected override void OnCreate()
{
#if RUKHANKA_WITH_NETCODE
bool isServer = World.IsServer();
if (isServer)
Enabled = false;
#endif
}
/////////////////////////////////////////////////////////////////////////////////
protected override void OnUpdate()
{
var acc = AnimationCullingConfig.Instance;
if (acc == null)
return;
if (!SystemAPI.TryGetSingletonRW<AnimationCullingContext>(out var actx))
{
var newContext = new AnimationCullingContext();
newContext.cullingPlanes = new (32, Allocator.Persistent);
newContext.cullingVolumePlaneRanges = new (32, Allocator.Persistent);
newContext.lodAffectors = new (32, Allocator.Persistent);
EntityManager.CreateSingleton(newContext, "Rukhanka.AnimationCullingContextSingleton");
actx = SystemAPI.GetSingletonRW<AnimationCullingContext>();
}
var cullingCameras = new NativeList<CullingCameraData>(acc.cullingCameras.Length, CheckedStateRef.WorldUpdateAllocator);
foreach (var cullingCam in acc.cullingCameras)
{
AddCullingCamera(cullingCam, ref cullingCameras);
}
var shadowCastingLights = new NativeList<LightData>(acc.shadowCastingLights.Length, CheckedStateRef.WorldUpdateAllocator);
foreach (var l in acc.shadowCastingLights)
{
AddShadowCastingLight(l, ref shadowCastingLights);
}
#if UNITY_EDITOR
if (acc.addEditorSceneCamera)
{
var sceneCam = SceneView.lastActiveSceneView?.camera;
AddCullingCamera(sceneCam, ref cullingCameras);
}
#endif
Dependency = BuildCullingContext(cullingCameras, shadowCastingLights, ref actx.ValueRW, acc, Dependency);
}
/////////////////////////////////////////////////////////////////////////////////
void AddShadowCastingLight(Light l, ref NativeList<LightData> shadowCastingLights)
{
if (l == null || l.shadows == LightShadows.None || !l.isActiveAndEnabled) return;
var lt = l.transform;
var ld = new LightData()
{
posOrDir = l.type == LightType.Directional ? new float4(-lt.forward, 0) : new float4(lt.position, 1),
range = l.range,
};
shadowCastingLights.Add(ld);
}
/////////////////////////////////////////////////////////////////////////////////
void AddCullingCamera(Camera cam, ref NativeList<CullingCameraData> cullingCameras)
{
if (cam == null) return;
var camTransform = cam.transform;
var ccd = new CullingCameraData()
{
pos = camTransform.position,
viewDir = camTransform.forward,
cullingMatrix = cam.cullingMatrix,
fieldOfView = cam.fieldOfView,
isOrthographic = cam.orthographic,
orthographicSize = cam.orthographicSize
};
cullingCameras.Add(ccd);
}
/////////////////////////////////////////////////////////////////////////////////
JobHandle BuildCullingContext(NativeList<CullingCameraData> cullingCameras, NativeList<LightData> shadowCastingLights, ref AnimationCullingContext actx, AnimationCullingConfig acc, JobHandle dependsOn)
{
#if (RUKHANKA_DEBUG_INFO && !RUKHANKA_NO_DEBUG_DRAWER)
SystemAPI.TryGetSingletonRW<Drawer>(out var dd);
#endif
var buildCullingContextJob = new BuildCullingContextJob()
{
cullingCameras = cullingCameras.AsArray(),
shadowCastingLights = shadowCastingLights.AsArray(),
lodBias = QualitySettings.lodBias,
actx = actx,
#if (RUKHANKA_DEBUG_INFO && !RUKHANKA_NO_DEBUG_DRAWER)
dd = dd.ValueRW,
#endif
};
var rv = buildCullingContextJob.Schedule(dependsOn);
#if (RUKHANKA_DEBUG_INFO && !RUKHANKA_NO_DEBUG_DRAWER)
actx.invisibleChunkColor = ColorTools.ToUint(acc.invisibleChunkColor);
actx.visibleChunkColor = ColorTools.ToUint(acc.visibleChunkColor);
actx.invisibleRendererColor = ColorTools.ToUint(acc.invisibleRendererColor);
actx.visibleRendererColor = ColorTools.ToUint(acc.visibleRendererColor);
actx.drawSceneBoundingBoxes = acc.drawSceneBoundingBoxes;
actx.drawCullingVolumes = acc.drawCullingVolumes;
actx.cullingVolumeColor = ColorTools.ToUint(acc.cullingVolumeColor);
#endif
return rv;
}
/////////////////////////////////////////////////////////////////////////////////
protected override void OnDestroy()
{
if (SystemAPI.TryGetSingleton<AnimationCullingContext>(out var actx))
{
actx.Dispose();
EntityManager.DestroyEntity(SystemAPI.GetSingletonEntity<AnimationCullingContext>());
}
}
}
}