//$ Copyright 2015-22, Code Respawn Technologies Pvt Ltd - All Rights Reserved $// using System; using System.Collections.Generic; using DungeonArchitect.Utils; using UnityEngine; using Object = UnityEngine.Object; namespace DungeonArchitect.SxEngine { public struct SxRenderContext { public Vector3 CameraPosition; } public class SxRenderCommand : IComparable { public Matrix4x4 AccumWorldTransform; public SxMesh Mesh; public SxMaterial Material; private float distanceSqToCam = 0; public SxRenderCommand(Matrix4x4 accumWorldTransform, SxMesh mesh, SxMaterial material) { AccumWorldTransform = accumWorldTransform; Mesh = mesh; Material = material; } public int CompareTo(SxRenderCommand b) { var a = this; var queueA = a.Material != null ? a.Material.RenderQueue : 0; var queueB = b.Material != null ? b.Material.RenderQueue : 0; if (queueA == queueB) { var depthBiasA = a.Material != null ? a.Material.DepthBias : 0; var depthBiasB = b.Material != null ? b.Material.DepthBias : 0; var da = a.distanceSqToCam + depthBiasA; var db = b.distanceSqToCam + depthBiasB; if (da == db) return 0; return da > db ? -1 : 1; } else { return queueA < queueB ? -1 : 1; } } public void UpdateDistanceToCam(Vector3 camLocation) { distanceSqToCam = (Matrix.GetTranslationDivW(ref AccumWorldTransform) - camLocation).sqrMagnitude; } } public class SxRenderCommandList { private List renderCommands = new List(); public SxRenderCommand[] Commands { get => renderCommands.ToArray(); } public void Add(SxRenderCommand command) { renderCommands.Add(command); } public void Sort(Vector3 camLocation) { UpdateDistanceFromCam(camLocation); renderCommands.Sort(); } private void UpdateDistanceFromCam(Vector3 camLocation) { foreach (var command in renderCommands) { command.UpdateDistanceToCam(camLocation); } } } public class SxRenderer { class ClearState { public bool ClearDepth = false; public bool ClearColor = false; public Color Color = Color.black; } public RenderTexture Texture { get; private set; } public SxCamera Camera { get; } = new SxCamera(); private ClearState clearState = new ClearState(); public float FOV { get; } = 75; public delegate void DrawDelegate(SxRenderContext context); public event DrawDelegate Draw; public void SetClearState(bool clearDepth, bool clearColor, Color color) { clearState.ClearDepth = clearDepth; clearState.ClearColor = clearColor; clearState.Color = color; } public SxRenderContext CreateRenderContext() { return new SxRenderContext { CameraPosition = Camera.Location }; } public float GetAspectRatio() { return Texture != null ? (float) Texture.width / Texture.height : 1.0f; } public void Render(Vector2 size, SxWorld world) { AcquireTexture(size); var oldRTT = RenderTexture.active; RenderTexture.active = Texture; GL.PushMatrix(); GL.LoadProjectionMatrix(Matrix4x4.Perspective(FOV, GetAspectRatio(), 0.1f, 100.0f)); if (clearState.ClearColor || clearState.ClearDepth) { GL.Clear(clearState.ClearDepth, clearState.ClearColor, clearState.Color); } var context = CreateRenderContext(); var renderCommandList = new SxRenderCommandList(); world.Draw(context, renderCommandList); renderCommandList.Sort(context.CameraPosition); Render(renderCommandList, Camera.ViewMatrix); if (Draw != null) { Draw.Invoke(context); } GL.PopMatrix(); RenderTexture.active = oldRTT; } public void Release() { ReleaseTexture(); } private void AcquireTexture(Vector2 size) { var width = Mathf.RoundToInt(size.x); var height = Mathf.RoundToInt(size.y); if (Texture != null && (Texture.width != width || Texture.height != height)) { ReleaseTexture(); } if (Texture == null) { Texture = new RenderTexture(Mathf.RoundToInt(size.x), Mathf.RoundToInt(size.y), 16, RenderTextureFormat.ARGB32); Texture.Create(); } } private void ReleaseTexture() { Texture.Release(); Object.DestroyImmediate(Texture); Texture = null; } class MergedMesh { public int DrawMode; public SxMaterial Material; public List Vertices = new List(); } class MergeMeshList { public MergedMesh ActiveMesh; private List mergedMeshes = new List(); public MergedMesh[] Meshes { get => mergedMeshes.ToArray(); } public MergeMeshList() { } public void CreateNew() { ActiveMesh = new MergedMesh(); mergedMeshes.Add(ActiveMesh); } } public void RenderDefault(SxRenderCommandList renderCommandList, Matrix4x4 viewMatrix) { foreach (var command in renderCommandList.Commands) { command.Material.Assign(); GL.modelview = viewMatrix * command.AccumWorldTransform; foreach (var entry in command.Mesh.Sections) { var section = entry.Value; GL.Begin(section.DrawMode); foreach (var vertex in section.Vertices) { GL.Color(vertex.Color); GL.TexCoord(vertex.UV0); var p = vertex.Position; GL.Vertex3(p.x, p.y, p.z); } GL.End(); } } } public void RenderMerged(SxRenderCommandList renderCommandList, Matrix4x4 viewMatrix) { GL.modelview = viewMatrix; // Merge similar meshes var mergedMeshes = new MergeMeshList(); foreach (var command in renderCommandList.Commands) { GenerateMergedMeshes(command, mergedMeshes); } // draw the merged meshes foreach (var mesh in mergedMeshes.Meshes) { mesh.Material.Assign(); GL.Begin(mesh.DrawMode); foreach (var vertex in mesh.Vertices) { GL.Color(vertex.Color); GL.TexCoord(vertex.UV0); var p = vertex.Position; GL.Vertex3(p.x, p.y, p.z); } GL.End(); } } public void Render(SxRenderCommandList renderCommandList, Matrix4x4 viewMatrix) { RenderDefault(renderCommandList, viewMatrix); //RenderMerged(renderCommandList, viewMatrix); } void GenerateMergedMeshes(SxRenderCommand command, MergeMeshList mergedMeshes) { if (command.Material == null || command.Mesh == null) return; foreach (var entry in command.Mesh.Sections) { var section = entry.Value; bool createNewMesh = mergedMeshes.ActiveMesh == null || mergedMeshes.ActiveMesh.Material != command.Material || mergedMeshes.ActiveMesh.DrawMode != section.DrawMode; if (createNewMesh) { mergedMeshes.CreateNew(); mergedMeshes.ActiveMesh.Material = command.Material; mergedMeshes.ActiveMesh.DrawMode = section.DrawMode; } foreach (var vertex in section.Vertices) { var mergedVertex = new SxMeshVertex(); mergedVertex.Color = vertex.Color; mergedVertex.UV0 = vertex.UV0; var p = command.AccumWorldTransform * MathUtils.ToVector4(vertex.Position, 1); p /= p.w; mergedVertex.Position = p; mergedMeshes.ActiveMesh.Vertices.Add(mergedVertex); } } } } }