using System.Runtime.InteropServices; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using Unity.Profiling; namespace Obi { [StructLayout(LayoutKind.Sequential)] public struct ChainRendererData { public int modifierOffset; public float twistAnchor; public float twist; public uint usesOrientedParticles; public Vector4 scale; public ChainRendererData(int modifierOffset, float twistAnchor, float twist, Vector3 scale, bool usesOrientedParticles) { this.modifierOffset = modifierOffset; this.twistAnchor = twistAnchor; this.twist = twist; this.usesOrientedParticles = (uint)(usesOrientedParticles ? 1 : 0); this.scale = scale; } } public abstract class ObiChainRopeRenderSystem : RenderSystem { public Oni.RenderingSystemType typeEnum { get => Oni.RenderingSystemType.ChainRope; } public RendererSet renderers { get; } = new RendererSet(); // specify vertex count and layout protected VertexAttributeDescriptor[] layout = { new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 4), new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.Float32, 4), new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2), }; static protected ProfilerMarker m_SetupRenderMarker = new ProfilerMarker("SetupChainRopeRendering"); static protected ProfilerMarker m_RenderMarker = new ProfilerMarker("ChainRopeRendering"); protected ObiSolver m_Solver; protected List batchList = new List(); protected ObiNativeList rendererData; protected ObiNativeList chunkData; protected ObiNativeList modifiers; protected ObiNativeList elements; protected ObiNativeList instanceTransforms; protected ObiNativeList invInstanceTransforms; protected ObiNativeList instanceColors; public ObiChainRopeRenderSystem(ObiSolver solver) { m_Solver = solver; } public virtual void Dispose() { CleanupBatches(); DestroyLists(); } private void DestroyLists() { if (instanceTransforms != null) instanceTransforms.Dispose(); if (invInstanceTransforms != null) invInstanceTransforms.Dispose(); if (instanceColors != null) instanceColors.Dispose(); if (elements != null) elements.Dispose(); if (chunkData != null) chunkData.Dispose(); if (rendererData != null) rendererData.Dispose(); if (modifiers != null) modifiers.Dispose(); } private void CreateListsIfNecessary() { DestroyLists(); instanceTransforms = new ObiNativeList(); invInstanceTransforms = new ObiNativeList(); instanceColors = new ObiNativeList(); elements = new ObiNativeList(); chunkData = new ObiNativeList(); rendererData = new ObiNativeList(); modifiers = new ObiNativeList(); } private void CleanupBatches() { for (int i = 0; i < batchList.Count; ++i) batchList[i].Dispose(); batchList.Clear(); } private void GenerateBatches() { instanceTransforms.Clear(); invInstanceTransforms.Clear(); instanceColors.Clear(); elements.Clear(); rendererData.Clear(); chunkData.Clear(); modifiers.Clear(); // generate batches: for (int i = 0; i < renderers.Count; ++i) { var renderer = renderers[i]; if (renderer.linkMesh != null && renderer.linkMaterial != null) { renderer.renderParameters.layer = renderer.gameObject.layer; batchList.Add(new InstancedRenderBatch(i, renderer.linkMesh, renderer.linkMaterial, renderer.renderParameters)); } } // sort batches: batchList.Sort(); // append elements: for (int i = 0; i < batchList.Count; ++i) { var renderer = renderers[batchList[i].firstRenderer]; var rope = renderer.actor as ObiRopeBase; modifiers.AddRange(renderer.linkModifiers); rendererData.Add(new ChainRendererData(modifiers.count, renderer.twistAnchor, renderer.linkTwist, renderer.linkScale, rope.usesOrientedParticles)); batchList[i].firstInstance = elements.count; batchList[i].instanceCount = rope.elements.Count; // iterate trough elements, finding discontinuities as we go: for (int e = 0; e < rope.elements.Count; ++e) { elements.Add(new Vector2Int(rope.elements[e].particle1, rope.elements[e].particle2)); // At discontinuities, start a new chunk. if (e < rope.elements.Count - 1 && rope.elements[e].particle2 != rope.elements[e + 1].particle1) { chunkData.Add(new ChunkData(rendererData.count - 1, elements.count)); } } chunkData.Add(new ChunkData(rendererData.count - 1, elements.count)); } instanceTransforms.ResizeUninitialized(elements.count); invInstanceTransforms.ResizeUninitialized(elements.count); instanceColors.ResizeUninitialized(elements.count); } protected virtual void CloseBatches() { // Initialize each batch: for (int i = 0; i < batchList.Count; ++i) batchList[i].Initialize(); } public virtual void Setup() { using (m_SetupRenderMarker.Auto()) { CreateListsIfNecessary(); CleanupBatches(); GenerateBatches(); ObiUtils.MergeBatches(batchList); CloseBatches(); } } public abstract void Render(); public void Step() { } public void BakeMesh(ObiRopeChainRenderer renderer, ref Mesh mesh, bool transformToActorLocalSpace = false) { int index = renderers.IndexOf(renderer); for (int i = 0; i < batchList.Count; ++i) { var batch = batchList[i]; if (index >= batch.firstRenderer && index < batch.firstRenderer + batch.rendererCount) { batch.BakeMesh(renderers, renderer, chunkData, instanceTransforms, renderer.actor.actorSolverToLocalMatrix, ref mesh, transformToActorLocalSpace); return; } } } } }