Graduate lightning presentation into EveUnity

This commit is contained in:
Meta Vens 2026-07-12 01:27:17 +01:00
commit d6bdeb8fe9
20 changed files with 931 additions and 2 deletions

View file

@ -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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 25f11aab7bd34496af779bfab1c22e65
folderAsset: yes

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6849f3e40d0646ac992c9c2bc394cb93
folderAsset: yes

View file

@ -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: []

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 476f7453551d79b469dda2d6926b93fd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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<Node> _NodeBuffer;
RWStructuredBuffer<Vertex> _VertexBuffer;
RWStructuredBuffer<Bolt> _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;
}

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a974189fea3fe9a46a7859ef6ea8da09
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 4
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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: []

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c94ec17d8118c2749ad885c2b378ea01
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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<uint> _IndexBuffer;
StructuredBuffer<Vertex> _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<uint> _IndexBuffer;
StructuredBuffer<Vertex> _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
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21dd485e846b4d3a8213cf5551832776
timeCreated: 1612395387

View file

@ -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<Node>();
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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 044c2be5da0fc2340a09f856be731cea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 58
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 73ccd50b02d44a0185deea93c36ffb55
timeCreated: 1612367245

View file

@ -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<LightningCompute>();
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) ||

View file

@ -3,7 +3,8 @@
"rootNamespace": "GameCult.Eve.UnityScene",
"references": [
"GameCult.Eve.Surface",
"GameCult.Eve.PluginFields"
"GameCult.Eve.PluginFields",
"Unity.Mathematics"
],
"includePlatforms": [],
"excludePlatforms": [],

View file

@ -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<ComputeShader>(root + "Lightning.compute");
var material = AssetDatabase.LoadAssetAtPath<Material>(root + "Lightning.mat");
var host = new GameObject("lightning-import-proof");
try
{
var effect = host.AddComponent<LightningCompute>();
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);
}
}
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c20d5fe870774a2a9e664bdd12e6c726

View file

@ -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",