290 lines
10 KiB
C#
290 lines
10 KiB
C#
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.Profiling;
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
namespace ZC
|
|
{
|
|
public class TestRay : MonoBehaviour
|
|
{
|
|
[SerializeField] int frameRate = 5;
|
|
[SerializeField] int _currentFrameCount;
|
|
[SerializeField] int rayCount;
|
|
[SerializeField] [Header("需要检测的层级")] private LayerMask toHitMask;
|
|
[SerializeField] [Header("水平射线数")] private int horizontalRaysCount = 400;
|
|
[SerializeField] [Header("垂直射线数")] private int verticalRaysCount = 375;
|
|
[SerializeField] private Mesh _mesh;
|
|
[SerializeField] private Material _mat;
|
|
[SerializeField] private Camera _camera;
|
|
public ParticleSystem _particleSystem;
|
|
private PointsDrawer _pointsDrawer;
|
|
|
|
private NativeArray<RaycastHit> results;
|
|
private NativeArray<ParticleSystem.Particle> particles;
|
|
private NativeArray<RaycastCommand> commands;
|
|
private NativeArray<TransData> rayCaster;
|
|
private NativeArray<ZPointInfo> flattenedPointsInfo;
|
|
|
|
private bool _isRunning;
|
|
|
|
private List<Renderer> _renderers = new List<Renderer>(150000);
|
|
private NativeArray<int> _hitCountArray;
|
|
|
|
public void AddRenderer(Renderer renderer)
|
|
{
|
|
this._renderers.Add(renderer);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
// var o = new GameObject();
|
|
// for (var i = 0; i < 10000; i++)
|
|
// {
|
|
// var insideUnitSphere = UnityEngine.Random.insideUnitSphere * 100;
|
|
// var primitive = UnityEngine.GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
// primitive.transform.position = insideUnitSphere;
|
|
// primitive.transform.parent = o.transform;
|
|
// _renderers.Add(primitive.GetComponent<Renderer>());
|
|
// }
|
|
|
|
|
|
rayCount = horizontalRaysCount * verticalRaysCount;
|
|
_pointsDrawer = new PointsDrawer();
|
|
_pointsDrawer.Setup(_mesh, rayCount, 1000, _mat);
|
|
results = new NativeArray<RaycastHit>(rayCount, Allocator.Persistent);
|
|
commands = new NativeArray<RaycastCommand>(rayCount, Allocator.Persistent);
|
|
rayCaster = new NativeArray<TransData>(rayCount, Allocator.Persistent);
|
|
particles = new NativeArray<ParticleSystem.Particle>(rayCount, Allocator.Persistent);
|
|
flattenedPointsInfo = new NativeArray<ZPointInfo>(rayCount, Allocator.Persistent); // 初始化存储点信息的列表,预先分配空间
|
|
this._hitCountArray = new NativeArray<int>(1, Allocator.Persistent);
|
|
StartCoroutine(InitRays());
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
_pointsDrawer.OnDispose();
|
|
results.Dispose();
|
|
commands.Dispose();
|
|
rayCaster.Dispose();
|
|
particles.Dispose();
|
|
flattenedPointsInfo.Dispose();
|
|
_hitCountArray.Dispose();
|
|
StopAllCoroutines();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (Input.GetKeyDown(KeyCode.S))
|
|
{
|
|
Debug.Log("Start");
|
|
this._isRunning = true;
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.D))
|
|
{
|
|
Debug.Log("Stop");
|
|
this._isRunning = false;
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.K))
|
|
{
|
|
foreach (var renderer1 in this._renderers)
|
|
{
|
|
renderer1.enabled = false;
|
|
}
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.L))
|
|
{
|
|
foreach (var renderer1 in this._renderers)
|
|
{
|
|
renderer1.enabled = true;
|
|
}
|
|
}
|
|
|
|
if (this._isRunning)
|
|
{
|
|
RayTest();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将垂直FOV转成水平FOV
|
|
/// </summary>
|
|
/// <param name="verFOV">垂直FOV值</param>
|
|
/// <param name="aspect">屏幕分辨率比例</param>
|
|
/// <returns></returns>
|
|
public static float ConvertVerticalFOVToHorizontally(float verFOV, float aspect)
|
|
{
|
|
// 垂直fov角度转成弧度
|
|
float verFovRadian = verFOV * Mathf.Deg2Rad;
|
|
// 算出视野高度的一半
|
|
float camHalfHeight = Mathf.Tan(verFovRadian / 2);
|
|
// 算出水平视野的弧度
|
|
float horFOVRadian = Mathf.Atan(camHalfHeight * aspect) * 2;
|
|
// 将水平视野弧度转成角度
|
|
float horFOV = horFOVRadian * Mathf.Rad2Deg;
|
|
|
|
return horFOV;
|
|
}
|
|
|
|
IEnumerator InitRays()
|
|
{
|
|
var horizontalFov = ConvertVerticalFOVToHorizontally(this._camera.fieldOfView, this._camera.aspect);
|
|
var verticalFov = this._camera.fieldOfView;
|
|
|
|
float halfXAngle = horizontalFov / 2.0f;
|
|
float halfYAngle = verticalFov / 2.0f;
|
|
|
|
|
|
int index = 0;
|
|
for (int i = 0; i < this.horizontalRaysCount; i++)
|
|
{
|
|
float angleDeltaX = Mathf.Lerp(-halfXAngle, halfXAngle, i * 1f / horizontalRaysCount);
|
|
for (int j = 0; j < this.verticalRaysCount; j++)
|
|
{
|
|
float angleDeltaY = Mathf.Lerp(-halfYAngle, halfYAngle, j * 1f / verticalRaysCount);
|
|
|
|
rayCaster[index] = new TransData()
|
|
{
|
|
localPosition = Vector3.zero,
|
|
localRotation = Quaternion.Euler(-angleDeltaY, angleDeltaX, 0),
|
|
};
|
|
index++;
|
|
}
|
|
}
|
|
|
|
yield return null;
|
|
}
|
|
|
|
void RayTest()
|
|
{
|
|
Profiler.BeginSample("ExecuteJobs");
|
|
float3 transformPosition = this._camera.transform.position;
|
|
quaternion transformRotation = this._camera.transform.rotation;
|
|
BuildRaycastCommandJob rJob = new BuildRaycastCommandJob
|
|
{
|
|
rayCaster = this.rayCaster,
|
|
commands = this.commands,
|
|
queryParameters = new QueryParameters(this.toHitMask, false, QueryTriggerInteraction.Ignore, false),
|
|
parentPos = transformPosition,
|
|
parentRot = transformRotation
|
|
};
|
|
var handle1 = rJob.Schedule(this.rayCount, 1);
|
|
|
|
JobHandle handle2 = RaycastCommand.ScheduleBatch(rJob.commands, results, 100, dependsOn: handle1); //调度批量射线投射命令
|
|
|
|
_hitCountArray[0] = 0;
|
|
|
|
HandleHitResultJob jJob = new HandleHitResultJob
|
|
{
|
|
rayCaster = this.rayCaster,
|
|
hitResults = this.results,
|
|
frameInfo = this.flattenedPointsInfo,
|
|
parentRot = transformRotation,
|
|
parentPos = transformPosition,
|
|
hitCount = this._hitCountArray
|
|
};
|
|
|
|
var handle3 = jJob.Schedule(dependsOn: handle2);
|
|
handle3.Complete();
|
|
var hitCount = _hitCountArray[0];
|
|
Profiler.EndSample();
|
|
Profiler.BeginSample("RenderPoints");
|
|
var bounds = new Bounds(transformPosition, Vector3.one*1000);
|
|
// _pointsDrawer.Render(flattenedPointsInfo,hitCount,bounds, transformPosition);
|
|
PointsPublisher.pointPublisher.PushData(flattenedPointsInfo,hitCount,transformPosition,transformRotation ,bounds);
|
|
Profiler.EndSample();
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
struct BuildRaycastCommandJob : IJobParallelFor
|
|
{
|
|
[ReadOnly] public QueryParameters queryParameters;
|
|
[ReadOnly] public NativeArray<TransData> rayCaster;
|
|
[ReadOnly] public float3 parentPos;
|
|
[ReadOnly] public quaternion parentRot;
|
|
|
|
public NativeArray<RaycastCommand> commands;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
var transData = rayCaster[index];
|
|
// var position = math.rotate(parentRot, transData.localPosition) + parentPos;
|
|
var position = this.parentPos;
|
|
var rotation = math.mul(parentRot, transData.localRotation);
|
|
var forward = math.mul(rotation, math.forward());
|
|
commands[index] = new RaycastCommand(position, forward, this.queryParameters, 1000); // 准备 RaycastCommand
|
|
}
|
|
}
|
|
|
|
public struct TransData
|
|
{
|
|
public float3 localPosition;
|
|
public quaternion localRotation;
|
|
}
|
|
|
|
[BurstCompile]
|
|
struct HandleHitResultJob : IJob
|
|
{
|
|
[ReadOnly] public NativeArray<RaycastHit> hitResults;
|
|
[ReadOnly] public NativeArray<TransData> rayCaster;
|
|
[ReadOnly] public float3 parentPos;
|
|
[ReadOnly] public quaternion parentRot;
|
|
|
|
public NativeArray<ZPointInfo> frameInfo;
|
|
public NativeArray<int> hitCount;
|
|
|
|
public void Execute()
|
|
{
|
|
for (var index = 0; index < this.hitResults.Length; index++)
|
|
{
|
|
var hit = this.hitResults[index];
|
|
var isHit = hit.colliderInstanceID != 0;
|
|
if (!isHit)
|
|
{
|
|
continue; // 若未命中碰撞体,则跳过
|
|
}
|
|
|
|
var transData = this.rayCaster[index];
|
|
var quaternion = math.mul(parentRot, transData.localRotation);
|
|
var forward = math.mul(quaternion, math.forward());
|
|
var dir = math.abs(math.dot(hit.normal, forward));
|
|
this.frameInfo[this.hitCount[0]++] = new ZPointInfo(hit.point, dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region mD
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct ZPointInfo
|
|
{
|
|
private float3 _point;
|
|
private float _dir;
|
|
|
|
public ZPointInfo(float3 point, float dir)
|
|
{
|
|
this._point = point;
|
|
this._dir = dir;
|
|
}
|
|
|
|
public float3 point
|
|
{
|
|
get => this._point;
|
|
set => this._point = value;
|
|
}
|
|
|
|
}
|
|
|
|
#endregion
|
|
} |