Files
jelito/Assets/jelycho/Code/Ropes/GutsRenderer.cs
2025-08-20 05:06:00 +02:00

198 lines
8.1 KiB
C#

using System;
using System.Collections.Generic;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using TriInspector;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
namespace RebootReality.jelycho.Ropes {
public class GutsRenderer : MonoBehaviour {
[SerializeField] RopesManager m_RopesManager;
[SerializeField] float m_RenderDistance = 100.0f;
[SerializeField] Material m_Material;
[SerializeField] int m_Layer;
[SerializeField] GutMeshGenerationConfig m_GutMeshGenerationConfig = new GutMeshGenerationConfig {
radius = 0.2f,
resolution = 16
};
readonly List<Mesh> m_Meshes = new List<Mesh>();
[ShowInInspector, TriInspector.ReadOnly]
int m_RenderCount = 0;
void OnDestroy() {
foreach (Mesh mesh in m_Meshes) {
Destroy(mesh);
}
m_Meshes.Clear();
}
void LateUpdate() {
m_RenderCount = 0;
for (int ropeIndex = 0; ropeIndex < m_RopesManager.RopesCount; ++ropeIndex) {
NativeArray<float3> ropePositions = m_RopesManager.PeekRopePositions(ropeIndex);
if (ropePositions.Length < 2) {
continue;
}
float distanceSquared = math.distancesq(m_RopesManager.GetRopeBounds(ropeIndex).center,
RR.MainCamera.transform.position);
if (distanceSquared > m_RenderDistance * m_RenderDistance) {
continue;
}
if (!m_RopesManager.IsRopeBoundsInFrustum(ropeIndex, RR.MainCamera)) {
continue;
}
int vertexCount = m_GutMeshGenerationConfig.resolution * ropePositions.Length;
int indexCount = m_GutMeshGenerationConfig.resolution * (ropePositions.Length - 1) * 6;
GenerateGutVertexDataJob job = new GenerateGutVertexDataJob {
Config = m_GutMeshGenerationConfig,
RopePositions = ropePositions,
Positions = new NativeArray<float3>(vertexCount, Allocator.TempJob),
Normals = new NativeArray<float3>(vertexCount, Allocator.TempJob),
UVs0 = new NativeArray<float2>(vertexCount, Allocator.TempJob),
UVs1 = new NativeArray<float2>(vertexCount, Allocator.TempJob),
Indices = new NativeArray<int>(indexCount, Allocator.TempJob)
};
job.Schedule().Complete();
Mesh mesh = GetOrCreateMesh(ropeIndex);
mesh.Clear();
mesh.SetVertices(job.Positions);
mesh.SetNormals(job.Normals);
mesh.SetUVs(0, job.UVs0);
mesh.SetUVs(1, job.UVs1);
mesh.SetIndices(job.Indices, MeshTopology.Triangles, 0, false);
job.Positions.Dispose();
job.Normals.Dispose();
job.UVs0.Dispose();
job.UVs1.Dispose();
job.Indices.Dispose();
Graphics.DrawMesh(mesh,
transform.localToWorldMatrix,
m_Material,
m_Layer,
null,
0,
null,
UnityEngine.Rendering.ShadowCastingMode.On,
true);
m_RenderCount += 1;
}
}
Mesh GetOrCreateMesh(int ropeIndex) {
if (ropeIndex < m_Meshes.Count) {
return m_Meshes[ropeIndex];
}
Mesh mesh = new Mesh {
name = $"GutMesh_{ropeIndex}",
indexFormat = UnityEngine.Rendering.IndexFormat.UInt32
};
mesh.MarkDynamic();
m_Meshes.Add(mesh);
return mesh;
}
[BurstCompile]
struct GenerateGutVertexDataJob : IJob {
[Unity.Collections.ReadOnly] public GutMeshGenerationConfig Config;
[Unity.Collections.ReadOnly] public NativeArray<float3> RopePositions;
[WriteOnly] public NativeArray<float3> Positions;
[WriteOnly] public NativeArray<float3> Normals;
[WriteOnly] public NativeArray<float2> UVs0;
public NativeArray<float2> UVs1;
[WriteOnly] public NativeArray<int> Indices;
public void Execute() {
float ropeLength = 0f;
for (int ropePositionIndex = 0; ropePositionIndex < RopePositions.Length; ++ropePositionIndex) {
bool isLast = ropePositionIndex >= RopePositions.Length - 1;
float3 start = RopePositions[ropePositionIndex];
float3 end = isLast ?
RopePositions[ropePositionIndex - 1] :
RopePositions[ropePositionIndex + 1];
float3 forward = math.normalize(end - start);
if (isLast) {
forward = -forward;
}
float3 right = math.normalize(math.cross(forward, math.abs(forward.x) > 0.99f ? new float3(0, 1, 0) : new float3(1, 0, 0)));
float3 up = math.cross(forward, right);
for (int i = 0; i < Config.resolution; ++i) {
float angle = (float) i / Config.resolution * math.PI * 2f;
float3 offset = (math.cos(angle) * right + math.sin(angle) * up) * Config.radius;
int vertexIndex = ropePositionIndex * Config.resolution + (i % Config.resolution);
Positions[vertexIndex] = start + offset;
Normals[vertexIndex] = math.normalize(offset);
UVs0[vertexIndex] = new float2((float) i / (Config.resolution - 1),
ropePositionIndex % 2 == 0 ? 0 : 1);
UVs1[vertexIndex] = new float2(ropeLength,
ropeLength);
}
ropeLength += math.distance(start, end);
}
for (int ropePositionIndex = 0; ropePositionIndex < RopePositions.Length; ++ropePositionIndex) {
for (int i = 0; i < Config.resolution; ++i) {
int vertexIndex = ropePositionIndex * Config.resolution + (i % Config.resolution);
UVs1[vertexIndex] = new float2(UVs1[vertexIndex].x,
ropeLength);
}
}
int index = 0;
for (int ropePositionIndex = 0; ropePositionIndex < RopePositions.Length - 1; ++ropePositionIndex) {
for (int i = 0; i < Config.resolution; ++i) {
int current = ropePositionIndex * Config.resolution + i;
int next = ropePositionIndex * Config.resolution + (i + 1) % Config.resolution;
int currentNextRow = (ropePositionIndex + 1) * Config.resolution + i;
int nextNextRow = (ropePositionIndex + 1) * Config.resolution + (i + 1) % Config.resolution;
Indices[index++] = current;
Indices[index++] = next;
Indices[index++] = currentNextRow;
Indices[index++] = currentNextRow;
Indices[index++] = next;
Indices[index++] = nextNextRow;
}
}
}
}
[Serializable]
struct GutMeshGenerationConfig {
[Min(0.01f)] public float radius;
[Range(3, 64)] public int resolution;
}
}
}