One partial of the football stadium panel's evening presentation: builds a single static mesh from generated polygons for floodlight lenses, halos and light shafts, then attaches it through a ModelRenderer. Turf lighting itself comes from four lights elsewhere.
using Sandbox;
using Socer;
using System;
using System.Collections.Generic;
using System.Linq;
public sealed partial class FootballWorldPanel
{
ModelRenderer eveningLamps;
Model eveningLampModel;
int eveningLampBanks,eveningLampVertices;
// One static alpha batch: lit lenses, small feathered halos and short
// shafts at the fixtures. Actual pools on the turf come from four lights;
// there are no painted circles or fullscreen colour/tint overlays.
void EnsureEveningLampPresentation()
{
foreach(var old in RenderScene.GetAllComponents<ModelRenderer>().Where(r=>r.GameObject.Name=="Socer evening lamp presentation").ToArray())
old.GameObject.Destroy();
var vertices=new List<Vertex>(4000);
int banks=0;
void Triangle(Vector3 a,Vector3 b,Vector3 c,byte aa,byte ab,byte ac)
{
vertices.Add(new Vertex(a,new Color32(229,243,255,aa)));
vertices.Add(new Vertex(b,new Color32(229,243,255,ab)));
vertices.Add(new Vertex(c,new Color32(229,243,255,ac)));
}
void Lens(Vector3 centre,Vector3 horizontal,Vector3 vertical,float width,float height)
{
var u=horizontal*(width*.5f*Units);var v=vertical*(height*.5f*Units);
Triangle(centre-u-v,centre+u-v,centre+u+v,255,255,255);
Triangle(centre-u-v,centre+u+v,centre-u+v,255,255,255);
}
void Halo(Vector3 centre,Vector3 horizontal,Vector3 vertical,float radius,byte alpha)
{
for(int i=0;i<16;i++)
{
float a=i*MathF.Tau/16,b=(i+1)*MathF.Tau/16;
var outerA=centre+(horizontal*MathF.Cos(a)+vertical*MathF.Sin(a))*(radius*Units);
var outerB=centre+(horizontal*MathF.Cos(b)+vertical*MathF.Sin(b))*(radius*Units);
Triangle(centre,outerA,outerB,alpha,0,0);
}
}
void Shaft(Vector3 from,Vector3 target,float length,float radius,byte alpha)
{
var direction=(target-from).Normal;var end=from+direction*(length*Units);
var across=Vector3.Cross(direction,Vector3.Up).Normal;
var other=Vector3.Cross(across,direction).Normal;
foreach(var axis in new[]{across,other})
{
// A narrow centre strip with transparent edges avoids a solid
// triangular panel when the camera pans past the stadium.
float[] bands={-1,-.25f,.25f,1};
for(int band=0;band<3;band++)
{
var a=from+axis*(bands[band]*.10f*Units);
var b=from+axis*(bands[band+1]*.10f*Units);
var c=end+axis*(bands[band+1]*radius*Units);
var d=end+axis*(bands[band]*radius*Units);
byte lo=band==0?(byte)0:alpha,hi=band==2?(byte)0:alpha;
Triangle(a,b,c,lo,hi,0);Triangle(a,c,d,lo,0,0);
}
}
}
for(int i=0;i<4;i++)
{
banks++;
bool east=(i&1)!=0,south=(i&2)!=0;
var mast=new Point(east?115:-10,south?76:-8);
for(int col=0;col<5;col++)for(int row=0;row<2;row++)
{
var at=WorldPoint(mast+new Point((col-2)*.72f,south?-.375f:.375f),21.55f+row*.85f);
Lens(at,Vector3.Forward,Vector3.Up,.48f,.52f);
}
var centre=WorldPoint(mast+new Point(0,south?-.39f:.39f),22);
Halo(centre,Vector3.Forward,Vector3.Up,1.35f,45);
Shaft(centre,WorldPoint(new Point(east?82:23,south?47:21),0),11,1.0f,17);
}
foreach(float x in new[]{6f,19,32,45,60,73,86,99})
{
banks++;
var centre=WorldPoint(new Point(x,-13.83f),7.71f);
for(int lens=0;lens<3;lens++)
Lens(centre+Vector3.Forward*((lens-1)*.24f*Units),Vector3.Forward,Vector3.Up,.22f,.25f);
Halo(centre,Vector3.Forward,Vector3.Up,.8f,65);
Shaft(centre,WorldPoint(new Point(x,-3.5f),0),5.2f,.54f,22);
}
var bounds=new BBox(WorldPoint(new Point(-13,79),0),WorldPoint(new Point(118,-22),24));
var mesh=new Mesh(DynamicGeometryMaterial);mesh.CreateVertexBuffer(vertices.Count,vertices);mesh.Bounds=bounds;
eveningLampModel=Model.Builder.AddMesh(mesh).WithViewBounds(bounds).Create();
eveningLamps=Create(eveningLampModel,"Socer evening lamp presentation");
eveningLamps.Tint=Color.White;eveningLamps.RenderType=ModelRenderer.ShadowRenderType.Off;
eveningLampBanks=banks;eveningLampVertices=vertices.Count;
}
}