diff --git a/packages/org.gamecult.eve.unity-scene/README.md b/packages/org.gamecult.eve.unity-scene/README.md index e7d4cde..d93183b 100644 --- a/packages/org.gamecult.eve.unity-scene/README.md +++ b/packages/org.gamecult.eve.unity-scene/README.md @@ -21,6 +21,9 @@ Retained provider feedback is lowered without becoming Unity authority. The generic `EveUnityShotTrajectoryRenderer` renders provider-authored origin, endpoint, duration, and hit outcome, then expires the visual locally. It never uses Unity collision to infer damage or report a hit back to the provider. +The reusable `LightningCompute` renderer and its shader/material bundle live in +this runtime package with their original Unity GUIDs. Provider asset bundles may +therefore reference the effect without importing Aetheria gameplay code. `fields.surface` projection consumes the plugin-owned `org.gamecult.eve.plugin-fields` contracts. `EveFieldsSplatRasterizer` owns the diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects.meta new file mode 100644 index 0000000..79073b0 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 25f11aab7bd34496af779bfab1c22e65 +folderAsset: yes diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning.meta new file mode 100644 index 0000000..b0430e0 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6849f3e40d0646ac992c9c2bc394cb93 +folderAsset: yes diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning 1.mat b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning 1.mat new file mode 100644 index 0000000..37619c3 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning 1.mat @@ -0,0 +1,85 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Lightning 1 + m_Shader: {fileID: 4800000, guid: 21dd485e846b4d3a8213cf5551832776, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BranchIntensity: 10 + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Intensity: 100 + - _LeaderIntensity: 100 + - _LeaderProgress: 2 + - _LeaderSize: 0.025 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _EndColor: {r: 1, g: 0.049999997, b: 0.049999997, a: 1} + - _StartColor: {r: 1, g: 0.20970267, b: 0.051643193, a: 1} + m_BuildTextureStacks: [] diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning 1.mat.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning 1.mat.meta new file mode 100644 index 0000000..9c2c07c --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning 1.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 476f7453551d79b469dda2d6926b93fd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.compute b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.compute new file mode 100644 index 0000000..0750006 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.compute @@ -0,0 +1,139 @@ +// Each #kernel tells which function to compile; you can have many kernels +#pragma kernel UpdateNodes +#pragma kernel CreateWidth + +#define NUM_THREAD_X 64 + +#include "LightningVertex.cginc" +#include "Assets/Plugins/GPU Noise/SimplexNoise2D.cginc" + +struct Node +{ + float3 pos; + int bolt; + float time; +}; + +struct Bolt +{ + float3 startPos; + float3 endPos; + float startTime; + int startIndex; + float3 startOffset; + float3 endOffset; +}; + +RWStructuredBuffer _NodeBuffer; +RWStructuredBuffer _VertexBuffer; +RWStructuredBuffer _BoltBuffer; + +float _NoiseAmplitude; +float _NoiseFrequency; +float _NoiseGain; +float _NoiseLacunarity; +float _Time; +float _StartWidth; +float _EndWidth; +int _TrunkNodeCount; +//int _BranchCount; +int _BranchNodeCount; +float3 _CameraPos; // for perspective camera + +float fbm(float2 p) +{ + float freq = _NoiseFrequency, amp = _NoiseAmplitude; + float sum = 0; + for(int i = 0; i < 4; i++) + { + sum += snoise(p * freq) * amp; + freq *= _NoiseLacunarity; + amp *= _NoiseGain; + } + return sum; +} + +float3 fbm3(float2 p) +{ + return float3( + fbm(p), + fbm(p+10), + fbm(p+20)); +} + +inline float3 calcToCameraDir(float3 pos) +{ + return normalize(_CameraPos - pos); +} + +[numthreads(NUM_THREAD_X,1,1)] +void UpdateNodes (uint3 id : SV_DispatchThreadID) +{ + int idx = id.x; + + Node node = _NodeBuffer[idx]; + Bolt bolt = _BoltBuffer[node.bolt]; + + float3 samplePos = lerp(bolt.startPos, bolt.endPos, node.time); + + float distance = length(bolt.startPos - samplePos); + + node.pos = samplePos + fbm3(float2(distance, _Time + node.bolt * 10)); + if(node.bolt == 0) node.pos += bolt.startOffset * max(.5 - node.time, 0) * 2 + bolt.endOffset * max(node.time - .5, 0) * 2; + else node.pos += bolt.startOffset; + + _NodeBuffer[idx] = node; +} + +[numthreads(NUM_THREAD_X,1,1)] +void CreateWidth (uint3 id : SV_DispatchThreadID) +{ + int idx = id.x; + + Vertex v0 = GetDefaultVertex(); + Vertex v1 = GetDefaultVertex(); + + Node node = _NodeBuffer[idx]; + Bolt bolt = _BoltBuffer[node.bolt]; + + float3 pos = node.pos; + + float3 dirPrev = float3(0,0,0); + float3 dirNext = float3(0,0,0); + + if ( idx!=bolt.startIndex ) + { + Node nodePrev = _NodeBuffer[idx-1]; + dirPrev = normalize(pos - nodePrev.pos); + } + + bool enableNext; + if(idx < _TrunkNodeCount) + enableNext = idx < _TrunkNodeCount - 1; + else enableNext = (idx - _TrunkNodeCount) % _BranchNodeCount < _BranchNodeCount - 1; + if ( enableNext ) + { + Node nodeNext = _NodeBuffer[idx+1]; + dirNext = normalize(nodeNext.pos - pos); + } + + float3 dir = normalize(dirPrev + dirNext); + + float width; + if(idx < _TrunkNodeCount) width = lerp(_StartWidth, _EndWidth, node.time); + else width = (1-node.time) * lerp(_StartWidth, _EndWidth, bolt.startTime); + + float3 toCameraDir = calcToCameraDir(pos); + float3 right = normalize(cross(dir, toCameraDir)) * width * 0.5f; + + float uvx = bolt.startTime + node.time * (1-bolt.startTime); + v0.pos = pos - right; + v0.uv = float2(uvx, 0 + node.bolt); + + v1.pos = pos + right; + v1.uv = float2(uvx, 1 + node.bolt); + + uint vId = idx * 2; + _VertexBuffer[vId] = v0; + _VertexBuffer[vId+1] = v1; +} diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.compute.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.compute.meta new file mode 100644 index 0000000..684669c --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.compute.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a974189fea3fe9a46a7859ef6ea8da09 +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 4 + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.mat b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.mat new file mode 100644 index 0000000..8957b7b --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.mat @@ -0,0 +1,85 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Lightning + m_Shader: {fileID: 4800000, guid: 21dd485e846b4d3a8213cf5551832776, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BranchIntensity: 10 + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Intensity: 10 + - _LeaderIntensity: 100 + - _LeaderProgress: 0.81259537 + - _LeaderSize: 0.025 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _EndColor: {r: 0.062478006, g: 0.11552096, b: 0.801, a: 0} + - _StartColor: {r: 0.83699995, g: 0.043821972, b: 0.57845026, a: 1} + m_BuildTextureStacks: [] diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.mat.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.mat.meta new file mode 100644 index 0000000..6dce22c --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c94ec17d8118c2749ad885c2b378ea01 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.shader b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.shader new file mode 100644 index 0000000..d01a958 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.shader @@ -0,0 +1,150 @@ +Shader "Aetheria/GPU Lightning" { +Properties { + _StartColor("StartColor", Color) = (1,1,1,1) + _EndColor("EndColor", Color) = (0,0,0,1) + _Intensity("Intensity", Float) = 1 + _LeaderIntensity("LeaderIntensity", Float) = 10 + _LeaderProgress("Leader Progress", Float) = 1 + _LeaderSize("Leader Size", Float) = .01 + _BranchIntensity("Branch Intensity", Float) = 1 +} + +SubShader { +Pass{ + Tags { "RenderType"="Opaque" } + + CGPROGRAM + #pragma target 5.0 + + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + #include "LightningVertex.cginc" + #include "Assets/Shaders/Dither Functions.cginc" + + float _Intensity; + float _LeaderIntensity; + float _LeaderProgress; + float _LeaderSize; + float _BranchIntensity; + fixed4 _StartColor; + fixed4 _EndColor; + StructuredBuffer _IndexBuffer; + StructuredBuffer _VertexBuffer; + + Vertex GetVertex(uint indexBufferIdx) { + uint idx = _IndexBuffer[indexBufferIdx]; + return _VertexBuffer[idx]; + } + + struct vs_out { + float4 pos : SV_POSITION; + float4 col : COLOR; + float2 uv : TEXCOORD; + float4 screenPos : TEXCOORD1; + }; + + vs_out vert (uint id : SV_VertexID, uint iId : SV_InstanceID) + { + vs_out Out; + Vertex vtx = GetVertex(id); + + Out.pos = UnityObjectToClipPos(float4(vtx.pos, 1.0)); + Out.uv = vtx.uv; + Out.col = lerp(_StartColor, _EndColor, vtx.uv.x); + Out.screenPos = ComputeScreenPos(float4(vtx.pos, 1.0)); + + return Out; + } + + fixed4 frag (vs_out In) : COLOR0 + { + // Discard fragments that are further than the leader progress + clip(_LeaderProgress-In.uv.x); + + // Simple quadratic curve "texture" + float x = 2 * (In.uv.y % 1) - 1; + float a = 1 - x * x; + + float i = In.uv.y > 1 ? _BranchIntensity : _Intensity; + a *= lerp(i, _LeaderIntensity, smoothstep(_LeaderProgress - _LeaderSize * 1.5, _LeaderProgress - _LeaderSize, In.uv.x)); + + // Apply screen space dithering + float2 screenUV = In.screenPos.xy / In.screenPos.w; + ditherClip(screenUV, a); + + return In.col * a; + } + + ENDCG + + } +Pass{ + Name "ShadowCaster" + Tags { "LightMode" = "ShadowCaster" } + + CGPROGRAM + #pragma target 5.0 + + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + #include "LightningVertex.cginc" + #include "Assets/Shaders/Dither Functions.cginc" + + float _LeaderProgress; + float _LeaderSize; + StructuredBuffer _IndexBuffer; + StructuredBuffer _VertexBuffer; + + Vertex GetVertex(uint indexBufferIdx) { + uint idx = _IndexBuffer[indexBufferIdx]; + return _VertexBuffer[idx]; + } + + struct vs_out { + float4 pos : SV_POSITION; + float4 col : COLOR; + float2 uv : TEXCOORD; + float4 screenPos : TEXCOORD1; + }; + + vs_out vert (uint id : SV_VertexID, uint iId : SV_InstanceID) + { + vs_out Out; + Vertex vtx = GetVertex(id); + + Out.pos = UnityObjectToClipPos(float4(vtx.pos, 1.0)); + Out.uv = vtx.uv; + Out.col = 1; + Out.screenPos = ComputeScreenPos(float4(vtx.pos, 1.0)); + + return Out; + } + + fixed4 frag (vs_out In) : COLOR0 + { + // Discard fragments that are further than the leader progress + clip(_LeaderProgress-In.uv.x); + + // Simple quadratic curve "texture" + float x = 2 * (In.uv.y % 1) - 1; + float a = 1 - x * x; + + // Apply screen space dithering + float2 screenUV = In.screenPos.xy / In.screenPos.w; + ditherClip(screenUV, a); + + SHADOW_CASTER_FRAGMENT(In) + } + + ENDCG + + } +} + +Fallback Off +} + diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.shader.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.shader.meta new file mode 100644 index 0000000..9e37a87 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/Lightning.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 21dd485e846b4d3a8213cf5551832776 +timeCreated: 1612395387 \ No newline at end of file diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningCompute.cs b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningCompute.cs new file mode 100644 index 0000000..6167fdc --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningCompute.cs @@ -0,0 +1,355 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Rendering; +using Unity.Mathematics; +using static Unity.Mathematics.math; +using static Unity.Mathematics.noise; +using Random = UnityEngine.Random; + +public class LightningCompute : MonoBehaviour +{ + public Camera Camera; + + [Header("Bolt Properties")] + public Vector3 StartPosition; + public Vector3 EndPosition; + public float StartWidth; + public float EndWidth; + public float BranchLength; + public float MorphSpeed; + public float NoiseAmplitude; + public float NoiseFrequency; + public float NoiseGain; + public float NoiseLacunarity; + public bool FixedEndpoint; + public bool Animate; + + [Header("Animation Properties")] + public float LeaderWidth; + public float LeaderDuration; + public float LeaderIntensity; + public float PulseDuration; + public float PulseIntensity; + public float PulseWidth; + public AnimationCurve PulseTrunkIntensityCurve; + public AnimationCurve PulseBranchIntensityCurve; + public AnimationCurve PulseWidthCurve; + + [Header("Buffer Sizes")] + public int TrunkNodeCount; + public int BranchCount; + public int BranchNodeCount; + + [Header("Shaders")] + public Material RenderMaterial; + public ComputeShader ComputeShader; + + public bool DebugDrawNodes; + public bool DebugDrawVertices; + + public Action OnLeaderComplete; + public Action OnPulseComplete; + + private Transform _cameraTransform; + + private ComputeBuffer _nodeBuffer; + private ComputeBuffer _vertexBuffer; + private ComputeBuffer _indexBuffer; + private ComputeBuffer _boltBuffer; + private Bolt[] _bolts; + private Vector3[] _branchEndpoints; + + private const int GROUP_SIZE = 64; + private int _updateKernel; + private int _createWidthKernel; + private float _animationStartTime; + private float _time; + private bool _leaderComplete; + private bool _pulseComplete; + private bool _initialized; + private Material _materialInstance; + + private int NodeBufferSize => TrunkNodeCount + BranchCount * BranchNodeCount; + private int VertexBufferSize => NodeBufferSize * 2; + private int IndexBufferSize => (NodeBufferSize - (BranchCount + 1)) * 6; + + public struct Node + { + public Vector3 pos; + public int bolt; + public float time; + } + + public struct Vertex + { + public Vector3 pos; + public Vector2 uv; + } + + public struct Bolt + { + public Vector3 startPos; + public Vector3 endPos; + public float startTime; + public int startIndex; + public Vector3 startOffset; + public Vector3 endOffset; + } + + void Start() + { + Initialize(); + _materialInstance = Instantiate(RenderMaterial); + } + + void Initialize() + { + if (_initialized) return; + _initialized = true; + + if (!Camera) Camera = Camera.main; + if (!Camera) throw new InvalidOperationException("EveUnity lightning presentation requires an active camera."); + _cameraTransform = Camera.transform; + _updateKernel = ComputeShader.FindKernel("UpdateNodes"); + _createWidthKernel = ComputeShader.FindKernel("CreateWidth"); + + _nodeBuffer = new ComputeBuffer(NodeBufferSize, Marshal.SizeOf(typeof(Node))); + var nodeList = new List(); + for (int i = 0; i < TrunkNodeCount; i++) + { + nodeList.Add(new Node{bolt = 0, pos = Vector3.zero, time = (float)i/(TrunkNodeCount-1)}); + } + + for (int branch = 0; branch < BranchCount; branch++) + { + for (int branchNode = 0; branchNode < BranchNodeCount; branchNode++) + { + nodeList.Add(new Node{bolt = branch+1, pos = Vector3.zero, time = (float)branchNode/(BranchNodeCount-1)}); + } + } + + _nodeBuffer.SetData(nodeList.ToArray()); + + + _boltBuffer = new ComputeBuffer(BranchCount + 1, Marshal.SizeOf(typeof(Bolt))); + _branchEndpoints = new Vector3[BranchCount]; + _bolts = new Bolt[BranchCount + 1]; + _bolts[0] = new Bolt + { + startPos = StartPosition, + endPos = EndPosition, + startTime = 0 + }; + + _boltBuffer.SetData(_bolts); + + _vertexBuffer = new ComputeBuffer(VertexBufferSize, Marshal.SizeOf(typeof(Vertex))); // 1 node to 2 vtx(left,right) + _vertexBuffer.SetData(Enumerable.Repeat(default(Vertex), _vertexBuffer.count).ToArray()); + + var indexData = new int[IndexBufferSize]; + var index = 0; + for (var iNode = 0; iNode < TrunkNodeCount - 1; iNode++) + { + var offset = iNode * 2; + indexData[index++] = 0 + offset; + indexData[index++] = 1 + offset; + indexData[index++] = 2 + offset; + indexData[index++] = 2 + offset; + indexData[index++] = 1 + offset; + indexData[index++] = 3 + offset; + } + for (int iBranch = 0; iBranch < BranchCount; iBranch++) + { + for (int iBranchNode = 0; iBranchNode < BranchNodeCount - 1; iBranchNode++) + { + var offset = iBranchNode * 2 + (TrunkNodeCount) * 2 + iBranch * (BranchNodeCount) * 2; + indexData[index++] = 0 + offset; + indexData[index++] = 1 + offset; + indexData[index++] = 2 + offset; + indexData[index++] = 2 + offset; + indexData[index++] = 1 + offset; + indexData[index++] = 3 + offset; + } + } + + _indexBuffer = new ComputeBuffer(indexData.Length, Marshal.SizeOf(typeof(uint))); // 1 node to 2 triangles(6vertexs) + _indexBuffer.SetData(indexData); + } + + public void StartAnimation() + { + Initialize(); + _animationStartTime = Time.time; + _time = Random.value * 1000; + _leaderComplete = false; + _pulseComplete = false; + + for (int i = 0; i < BranchCount; i++) + { + _branchEndpoints[i] = Random.insideUnitSphere * BranchLength; + _bolts[i+1] = new Bolt + { + startTime = Random.value * .8f + }; + } + } + + float fbm(float2 p) + { + float freq = NoiseFrequency, amp = NoiseAmplitude; + float sum = 0; + for(int i = 0; i < 4; i++) + { + sum += snoise(p * freq) * amp; + freq *= NoiseLacunarity; + amp *= NoiseGain; + } + return sum; + } + + float3 fbm3(float2 p) + { + return float3( + fbm(p), + fbm(p+10), + fbm(p+20)); + } + + void Update() + { + _time += Time.deltaTime * MorphSpeed; + + if(Animate) + { + var leaderLerp = (Time.time - _animationStartTime) / LeaderDuration; + if (leaderLerp > 1 && !_leaderComplete) + { + OnLeaderComplete?.Invoke(); + _leaderComplete = true; + } + + var pulseLerp = (Time.time - _animationStartTime - LeaderDuration) / PulseDuration; + if (pulseLerp > 1 && !_pulseComplete) + { + OnPulseComplete?.Invoke(); + _pulseComplete = true; + } + ComputeShader.SetFloat("_StartWidth", leaderLerp < 1 ? LeaderWidth : StartWidth * PulseWidthCurve.Evaluate(pulseLerp)*PulseWidth); + ComputeShader.SetFloat("_EndWidth", leaderLerp < 1 ? LeaderWidth : EndWidth * PulseWidthCurve.Evaluate(pulseLerp)*PulseWidth); + + _materialInstance.SetFloat("_LeaderProgress", leaderLerp); + _materialInstance.SetFloat("_BranchIntensity", leaderLerp < 1 ? LeaderIntensity : PulseBranchIntensityCurve.Evaluate(pulseLerp) * PulseIntensity); + _materialInstance.SetFloat("_Intensity", leaderLerp < 1 ? LeaderIntensity : PulseTrunkIntensityCurve.Evaluate(pulseLerp) * PulseIntensity); + } + else + { + ComputeShader.SetFloat("_StartWidth", StartWidth); + ComputeShader.SetFloat("_EndWidth", EndWidth); + } + var diff = EndPosition - StartPosition; + var dist = diff.magnitude; + _bolts[0].startPos = StartPosition; + _bolts[0].startOffset = -(Vector3) fbm3(float2(0, _time)); + _bolts[0].endPos = EndPosition; + _bolts[0].endOffset = FixedEndpoint ? -(Vector3) fbm3(float2(dist, _time)) : Vector3.zero; + + for (int i = 0; i < BranchCount; i++) + { + var root = Vector3.Lerp(StartPosition, EndPosition, _bolts[i + 1].startTime); + var rootDist = _bolts[i + 1].startTime * dist; + _bolts[i + 1].startPos = + root + (Vector3) fbm3(float2(rootDist, _time)) + + _bolts[0].startOffset * (max(.5f - _bolts[i + 1].startTime, 0) * 2) + + _bolts[0].endOffset * (max(_bolts[i + 1].startTime - .5f, 0) * 2); + _bolts[i + 1].startOffset = -(Vector3) fbm3(float2(0, _time + (i + 1) * 10)); + _bolts[i + 1].endPos = root + _branchEndpoints[i]; + } + + _boltBuffer.SetData(_bolts); + + ComputeShader.SetBuffer(_updateKernel, "_NodeBuffer", _nodeBuffer); + ComputeShader.SetBuffer(_updateKernel, "_BoltBuffer", _boltBuffer); + + ComputeShader.SetFloat("_Time", _time); + ComputeShader.SetFloat("_NoiseAmplitude", NoiseAmplitude); + ComputeShader.SetFloat("_NoiseFrequency", NoiseFrequency); + ComputeShader.SetFloat("_NoiseGain", NoiseGain); + ComputeShader.SetFloat("_NoiseLacunarity", NoiseLacunarity); + + ComputeShader.Dispatch(_updateKernel, NodeBufferSize/GROUP_SIZE, 1, 1); + + ComputeShader.SetInt("_TrunkNodeCount", TrunkNodeCount); + ComputeShader.SetInt("_BranchNodeCount", BranchNodeCount); + ComputeShader.SetVector("_CameraPos", _cameraTransform.position); + + ComputeShader.SetBuffer(_createWidthKernel, "_NodeBuffer", _nodeBuffer); + ComputeShader.SetBuffer(_createWidthKernel, "_BoltBuffer", _boltBuffer); + ComputeShader.SetBuffer(_createWidthKernel, "_VertexBuffer", _vertexBuffer); + + ComputeShader.Dispatch(_createWidthKernel, NodeBufferSize/GROUP_SIZE, 1, 1); + + _materialInstance.SetBuffer("_IndexBuffer", _indexBuffer); + _materialInstance.SetBuffer("_VertexBuffer", _vertexBuffer); + Graphics.DrawProcedural( + _materialInstance, + new Bounds((EndPosition + StartPosition) / 2, (EndPosition - StartPosition).magnitude * Vector3.one), + MeshTopology.Triangles, + IndexBufferSize, + 1, + Camera, + null, + ShadowCastingMode.Off, + false, + 0); + // + // if(pulseLerp > 2) + // StartAnimation(); + } + + #region cleanup + void OnDestroy() + { + if(_indexBuffer!=null) + { + _indexBuffer.Release(); + _boltBuffer.Release(); + _nodeBuffer.Release(); + _vertexBuffer.Release(); + } + } + #endregion + + public void OnDrawGizmosSelected() + { + if (DebugDrawNodes && _nodeBuffer != null) + { + Gizmos.color = Color.magenta; + var data = new Node[_nodeBuffer.count]; + _nodeBuffer.GetData(data); + foreach(var node in data) + { + Gizmos.DrawWireSphere(node.pos, StartWidth); + }; + } + + if (DebugDrawVertices && _vertexBuffer != null) + { + Gizmos.color = Color.yellow; + var data = new Vertex[_vertexBuffer.count]; + _vertexBuffer.GetData(data); + + var num = _vertexBuffer.count / 2; + for(var i=0; i< num; ++i) + { + var v0 = data[2*i]; + var v1 = data[2*i +1]; + + Gizmos.DrawLine(v0.pos, v1.pos); + } + } + } +} diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningCompute.cs.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningCompute.cs.meta new file mode 100644 index 0000000..112175d --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningCompute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 044c2be5da0fc2340a09f856be731cea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 58 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningVertex.cginc b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningVertex.cginc new file mode 100644 index 0000000..ab45431 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningVertex.cginc @@ -0,0 +1,20 @@ +#ifndef LIGHTNING_VERTEX_INCLUDED +#define LIGHTNING_VERTEX_INCLUDED + +struct Vertex +{ + float3 pos; + float2 uv; +}; + +inline Vertex GetDefaultVertex() { + + Vertex ret; + ret.pos = (0).xxx; + ret.uv = (-1).xx; + + return ret; +} + + +#endif // LIGHTNING_VERTEX_INCLUDED diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningVertex.cginc.meta b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningVertex.cginc.meta new file mode 100644 index 0000000..cbc8cf8 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/LightningVertex.cginc.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 73ccd50b02d44a0185deea93c36ffb55 +timeCreated: 1612367245 \ No newline at end of file diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs index 183d78b..ecee9ca 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs @@ -62,6 +62,14 @@ namespace GameCult.Eve.UnityScene effect.transform.position = origin; var direction = endpoint - origin; if (direction.sqrMagnitude > 0.0001f) effect.transform.rotation = Quaternion.LookRotation(direction.normalized); + var lightning = effect.GetComponentInChildren(); + if (lightning != null) + { + lightning.StartPosition = origin; + lightning.EndPosition = endpoint; + lightning.Animate = true; + lightning.StartAnimation(); + } } var impactPrefab = ResolveRolePrefab("effect.impact." + receipt.ImpactKind); var travels = string.Equals(receipt.PresentationKind, "bolt", StringComparison.Ordinal) || diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef b/packages/org.gamecult.eve.unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef index 6651097..784339a 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef +++ b/packages/org.gamecult.eve.unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef @@ -3,7 +3,8 @@ "rootNamespace": "GameCult.Eve.UnityScene", "references": [ "GameCult.Eve.Surface", - "GameCult.Eve.PluginFields" + "GameCult.Eve.PluginFields", + "Unity.Mathematics" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityLightningEffectTests.cs b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityLightningEffectTests.cs new file mode 100644 index 0000000..31b4e25 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityLightningEffectTests.cs @@ -0,0 +1,32 @@ +using NUnit.Framework; +using UnityEditor; +using UnityEngine; + +namespace GameCult.Eve.UnityScene.Tests +{ + public sealed class EveUnityLightningEffectTests + { + [Test] + public void GraduatedLightningBundleLoadsWithoutAetheriaAssemblies() + { + const string root = "Packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/"; + var compute = AssetDatabase.LoadAssetAtPath(root + "Lightning.compute"); + var material = AssetDatabase.LoadAssetAtPath(root + "Lightning.mat"); + var host = new GameObject("lightning-import-proof"); + try + { + var effect = host.AddComponent(); + effect.ComputeShader = compute; + effect.RenderMaterial = material; + + Assert.That(compute, Is.Not.Null); + Assert.That(material, Is.Not.Null); + Assert.That(effect.GetType().Assembly.GetName().Name, Is.EqualTo("GameCult.Eve.UnityScene")); + } + finally + { + Object.DestroyImmediate(host); + } + } + } +} diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityLightningEffectTests.cs.meta b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityLightningEffectTests.cs.meta new file mode 100644 index 0000000..6163deb --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityLightningEffectTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c20d5fe870774a2a9e664bdd12e6c726 diff --git a/packages/org.gamecult.eve.unity-scene/package.json b/packages/org.gamecult.eve.unity-scene/package.json index 723bfe5..fd38100 100644 --- a/packages/org.gamecult.eve.unity-scene/package.json +++ b/packages/org.gamecult.eve.unity-scene/package.json @@ -7,7 +7,8 @@ "dependencies": { "org.gamecult.cultlib": "1.0.3", "org.gamecult.eve.plugin-fields": "0.1.1", - "org.gamecult.eve.surface": "0.1.0" + "org.gamecult.eve.surface": "0.1.0", + "com.unity.mathematics": "1.3.2" }, "keywords": [ "Eve",