Last active
May 2, 2019 10:34
-
-
Save antonkudin/ad2ccf97fd95861ca5190f28e0e27aa6 to your computer and use it in GitHub Desktop.
MegaSphere logo assembler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class logoAssembler : MonoBehaviour { | |
[SerializeField] bool debugMe; | |
[SerializeField] bool remakePixels; | |
[SerializeField] bool autoPlay; | |
[SerializeField][Range(0,10)] float autoDur = 10; | |
[Space] | |
[SerializeField][Range(0,1)] float transition; | |
[Space] | |
[SerializeField] Vector2 animSpotsFork = new Vector2(.11f,-3.4f); | |
[SerializeField] Vector2 animLinesFork = new Vector2(-.6f,-3.7f); | |
[Space] | |
[SerializeField] Sprite sprite; // sprite's texture needs to set to readable in import options | |
[SerializeField] Material material; | |
// main texture should be a gradient, with wrap set to clamp | |
// left and right most pixels are black, with white somewhere in middle (close to right for same look) | |
[Space] | |
public Gradient lineColors; // random colors for lines | |
public Gradient spotColors; // random colors for spots | |
public Gradient spotColorOverTime; // change (multiply) spots color over their lifetime | |
[Space] | |
[SerializeField] int totalOffsets = 20; // line presets | |
[SerializeField] int vertsPerOffset = 8; // how zaggy line needs to be | |
[SerializeField][Range(0.0625f,10f)] float minOffsetDist=10; | |
[SerializeField][Range(0.0625f,50f)] float maxOffsetDist=9.7f; | |
[SerializeField][Range(0.0625f,10f)] float cLifeOffset=3.93f; | |
[SerializeField][Range(0f,2f)] float particleOffsetScale=2f; | |
[Space] | |
[SerializeField] Vector2 lineOffsetFork=new Vector2(0,11.6f); | |
[SerializeField] float pixelSize = .0625f; | |
[Space] | |
[SerializeField] float uvDistScale=5.4f; | |
[SerializeField] Vector2 lineUVMin=new Vector2(0,.4f); | |
[SerializeField] Vector2 lineUVMax=new Vector2(1,.6f); | |
[Space] | |
[SerializeField] Vector2 spotOffsetFork=new Vector2(1,0); | |
[SerializeField] Vector2 spotUVMin=new Vector2(.985f,1); | |
[SerializeField] Vector2 spotUVMax=new Vector2(.985f,1); | |
[Space] | |
[SerializeField] float lineZDepth; | |
[SerializeField] float spotZDepth; | |
Mesh mesh; | |
MeshFilter mf; | |
MeshRenderer mr; | |
List<Vector3> verts; | |
List<Vector2> uvs; | |
List<int> tris; | |
List<Color> colors; | |
Vector2[][] offsets; | |
List<pixel> pixels; | |
float time; | |
[System.Serializable] | |
struct pixel{ | |
public float offset; | |
public float lifetime; | |
public List<Vector2> path; | |
public List<Vector3> lineVerts; | |
public List<int> lineTris; | |
public List<Vector2> lineUVs; | |
public bool hasLine; | |
public Color lineColor; | |
public Color spotColor; | |
} | |
// draw in scene | |
void OnDrawGizmos(){ | |
if(Application.isPlaying) return; | |
if(debugMe){ | |
if(autoPlay){ | |
// i make deltatime from time i get in another script (drawDebugs.time), but it will work with something like | |
// transition += 1/60f; | |
transition += Mathf.Clamp(drawDebugs.time-time,.0166f,.033f) / autoDur; | |
if(transition>1) | |
transition = transition.frac(); | |
time = drawDebugs.time; | |
} | |
updateMesh(); | |
}else cleanUp(); | |
} | |
// draw when running | |
void Update(){ | |
updateMesh(); | |
} | |
void initMe(){ | |
mf = GetComponent<MeshFilter>(); | |
if(mf==null) | |
mf=gameObject.AddComponent<MeshFilter>(); | |
mr = GetComponent<MeshRenderer>(); | |
if(mr==null) | |
mr=gameObject.AddComponent<MeshRenderer>(); | |
mr.sharedMaterial = material; | |
if(mesh==null) { | |
mesh = new Mesh(); | |
mesh.MarkDynamic(); | |
} | |
else | |
mesh.Clear(); | |
mf.sharedMesh = mesh; | |
if(verts==null) verts = new List<Vector3>(); | |
if(uvs==null) uvs = new List<Vector2>(); | |
if(tris==null) tris = new List<int>(); | |
if(colors==null) colors = new List<Color>(); | |
makeLinePresets(); | |
makePixels(); | |
} | |
void cleanUp(){ | |
if(mf!=null){ | |
DestroyImmediate(mf); | |
DestroyImmediate(GetComponent<MeshRenderer>()); | |
} | |
mesh = null; | |
verts = null; | |
tris = null; | |
uvs = null; | |
pixels = null; | |
colors = null; | |
} | |
void updateMesh(){ | |
if(mf==null || remakePixels) | |
initMe(); | |
// clear all mesh lists | |
verts.Clear(); | |
tris.Clear(); | |
uvs.Clear(); | |
colors.Clear(); | |
// where's the offset for lines and spots | |
float lineT = Mathf.Lerp(animLinesFork.x, animLinesFork.y, transition); | |
float spotT = Mathf.Lerp(animSpotsFork.x, animSpotsFork.y, transition); | |
float halfPixel = pixelSize/2; | |
for(int l=0;l<pixels.Count;l++){ | |
var pixel = pixels[l]; | |
if(!pixel.hasLine) { | |
goto spot; | |
} | |
// adding a line to mesh | |
float pathOffset = lineT+pixel.offset; | |
float uOffset = Mathf.LerpUnclamped(lineOffsetFork.x,lineOffsetFork.y,pathOffset); | |
float uvMin = pixel.lineUVs[0].x + uOffset; | |
float uvMax = pixel.lineUVs[pixel.lineUVs.Count-1].x + uOffset; | |
// if this line is visible, add it to mesh | |
if(uvMax>0 && uvMin<1){ | |
// offset triangle indexes with current count | |
for(int i=0;i<pixel.lineTris.Count;i++) | |
tris.Add(pixel.lineTris[i]+verts.Count); | |
// pick line verts from pixel's cache | |
verts.AddRange(pixel.lineVerts); | |
// pick UVs from pixel's cache, but offset them with our timing, pick and set colors | |
var pUVs = pixel.lineUVs; | |
for(int i=0;i<pUVs.Count;i++){ | |
uvs.Add(new Vector2(pUVs[i].x+uOffset, pUVs[i].y)); | |
colors.Add(pixel.lineColor); | |
} | |
} | |
// adding a spot to mesh | |
spot:{ | |
// get our time offsets | |
pathOffset = spotT+pixel.offset; | |
uOffset = PowUp( Mathf.Clamp01( Mathf.LerpUnclamped(spotOffsetFork.x,spotOffsetFork.y,pathOffset)), 2); | |
// add spot mesh data if its visible | |
if(uOffset>0){ | |
// its just a quad | |
int vcount = verts.Count; | |
tris.Add(vcount); | |
tris.Add(vcount+1); | |
tris.Add(vcount+2); | |
tris.Add(vcount+3); | |
tris.Add(vcount); | |
tris.Add(vcount+2); | |
// position is picked from cached line | |
Vector2 pixPos = lerpOnPath(pixel.path, uOffset); | |
verts.Add(new Vector3(pixPos.x+halfPixel, pixPos.y+halfPixel,spotZDepth)); | |
verts.Add(new Vector3(pixPos.x+halfPixel, pixPos.y-halfPixel,spotZDepth)); | |
verts.Add(new Vector3(pixPos.x-halfPixel, pixPos.y-halfPixel,spotZDepth)); | |
verts.Add(new Vector3(pixPos.x-halfPixel, pixPos.y+halfPixel,spotZDepth)); | |
uvs.Add(spotUVMax); | |
uvs.Add(new Vector2(spotUVMax.x, spotUVMin.y)); | |
uvs.Add(spotUVMin); | |
uvs.Add(new Vector2(spotUVMin.x, spotUVMax.y)); | |
Color spotColor = pixel.spotColor * spotColorOverTime.Evaluate(uOffset); | |
colors.Add(spotColor); | |
colors.Add(spotColor); | |
colors.Add(spotColor); | |
colors.Add(spotColor); | |
} | |
} | |
} | |
// add all our lists to mesh if we have verticies or just disable renderer | |
if(verts.Count>0){ | |
mesh.Clear(); | |
mesh.SetVertices(verts); | |
mesh.SetUVs(0,uvs); | |
mesh.SetColors(colors); | |
mesh.SetTriangles(tris,0); | |
mesh.RecalculateBounds(); | |
mr.enabled=true; | |
}else{ | |
mr.enabled=false; | |
} | |
} | |
void makePixels(){ | |
if(pixels==null) | |
pixels = new List<pixel>(); | |
else | |
pixels.Clear(); | |
if(uvDistScale==0) uvDistScale=.0001f; | |
int width=(int)sprite.textureRect.width; | |
int height=(int)sprite.textureRect.height; | |
int ox=(int)sprite.textureRect.position.x; | |
int oy=(int)sprite.textureRect.position.y; | |
Vector2 max = sprite.size()/2; | |
Vector2 min = -max; | |
float halfPixel = pixelSize/2; | |
for (int x = 0; x < width; x++) | |
for (int y = 0; y < height; y++) { | |
// get pixel color value from sprite's texture | |
Color c = sprite.texture.GetPixel(x+ox, y+oy); | |
if(c.a>0) { // alpha>0 creates a spot/line | |
var newPixel = new pixel(); | |
newPixel.offset = c.r*cLifeOffset; | |
Vector2[] origPath = offsets[pixels.Count%totalOffsets]; | |
newPixel.path = new List<Vector2>(); | |
newPixel.hasLine = c.g>0; // green > 0 means line exist, othersize its just 1px spot | |
var finalPosition = new Vector2( | |
Mathf.Lerp(min.x, max.x, (float)x/(float)width), | |
Mathf.Lerp(min.y, max.y, (float)y/(float)height)); | |
// pixel path a spot will take. one of presets is picked and scaled | |
for(int p=0;p<origPath.Length;p++) | |
newPixel.path.Add(origPath[origPath.Length-1-p] * particleOffsetScale + finalPosition); | |
// cache line's mesh data | |
if(newPixel.hasLine){ | |
newPixel.lineColor = lineColors.Evaluate(Random.value); | |
newPixel.lineTris = new List<int>(); | |
newPixel.lineVerts = new List<Vector3>(); | |
newPixel.lineUVs = new List<Vector2>(); | |
float dist = 0; | |
int totalTris = (newPixel.path.Count-1)*6; | |
int t=0; | |
for(int p=0;p<newPixel.path.Count;p++){ | |
if(t<totalTris){ | |
int p2 = p*2; | |
newPixel.lineTris.Add(p2); | |
newPixel.lineTris.Add(p2+2); | |
newPixel.lineTris.Add(p2+1); | |
newPixel.lineTris.Add(p2+1); | |
newPixel.lineTris.Add(p2+2); | |
newPixel.lineTris.Add(p2+3); | |
t+=6; | |
} | |
if(p>0){ | |
dist += (newPixel.path[p]-newPixel.path[p-1]).magnitude/uvDistScale; | |
newPixel.lineUVs.Add(new Vector2(dist,lineUVMin.y)); | |
newPixel.lineUVs.Add(new Vector2(dist,lineUVMax.y)); | |
}else{ | |
newPixel.lineUVs.Add(new Vector2(lineUVMin.x,lineUVMin.y)); | |
newPixel.lineUVs.Add(new Vector2(lineUVMin.x,lineUVMax.y)); | |
} | |
newPixel.lineVerts.Add(new Vector3(0,0,lineZDepth)); | |
newPixel.lineVerts.Add(new Vector3(0,0,lineZDepth)); | |
} | |
wideLine(ref newPixel.path, ref newPixel.lineVerts, halfPixel, 0, newPixel.path.Count-1); | |
} | |
newPixel.spotColor = spotColors.Evaluate(Random.value); | |
pixels.Add(newPixel); | |
} | |
} | |
} | |
int offsetscacheID; // dumb optimization for tweaking | |
// make randomized broken line presets | |
void makeLinePresets() | |
{ | |
int id = totalOffsets*176 + vertsPerOffset*661 + (int)(maxOffsetDist*32) - (int)(minOffsetDist*32); | |
if(offsets!=null && offsetscacheID==id) return; | |
offsetscacheID = id; | |
offsets = new Vector2[totalOffsets][]; | |
for(int o = 0; o<totalOffsets; o++){ | |
offsets[o] = new Vector2[vertsPerOffset]; | |
Vector2 dir = new Vector2(Random.value>.5f?1:-1,Random.value>.5f?1:-1); | |
float dist = 0; | |
for(int v=0; v<vertsPerOffset; v++){ | |
if(v==0) | |
offsets[o][v] = Vector2.zero; | |
else{ | |
Vector2 move = random45Vector(dir); | |
offsets[o][v] = offsets[o][v-1] + move; | |
dist+=move.magnitude; | |
} | |
} | |
// normalize and bring to one length | |
for(int v=0; v<vertsPerOffset; v++) | |
offsets[o][v] = (offsets[o][v]/dist) * maxOffsetDist; | |
} | |
} | |
Vector2 random45Vector(Vector2 dir){ | |
if(Random.value>.5f && dir.x!=0) | |
return new Vector2( dir.x, Random.value>.5f?dir.y:0) * Random.Range(minOffsetDist,maxOffsetDist); | |
else if(dir.y!=0) | |
return new Vector2( Random.value>.5f?dir.x:0, dir.y) * Random.Range(minOffsetDist,maxOffsetDist); | |
else | |
return dir * Random.Range(minOffsetDist,maxOffsetDist); | |
} | |
// get poistion on a path with a float (0-1) | |
public static Vector2 lerpOnPath(List<Vector2> path, float value){ | |
if(path==null || path.Count==0) return Vector2.zero; | |
if(path.Count==1) return path[0]; | |
if(float.IsNaN(value)){ | |
Debug.Log("LerpOnPath: NAN value"); | |
return path[0]; | |
} | |
if(value>=1) return path[path.Count-1]; | |
if(value<=0) return path[0]; | |
float p = path.Count-1; | |
float v = value*p; | |
return Vector2.Lerp (path[Mathf.FloorToInt(v)], path[Mathf.CeilToInt(v)], Mathf.Repeat(value, 1/p)*p); | |
} | |
public static float PowUp(float value, float power){ | |
return 1-Mathf.Pow(1-value,power); | |
} | |
public static void wideLine(ref List<Vector2> points, ref List<Vector3> verts, float width, int start, int end, float mLimit=2){ | |
Vector2 pDir = Vector2.zero; | |
Vector2 dir = Vector2.zero; | |
for(int i = start; i<end; i++) { | |
Vector2 point = points[i]; | |
dir = points[i+1] - point; | |
var normal = new Vector2(-dir.y, dir.x).normalized; | |
int di = i*2; | |
if(i>start){ | |
Vector2 tn = (dir.normalized + pDir.normalized).normalized; | |
Vector2 mt = new Vector2(-tn.y,tn.x); | |
float dot = Vector2.Dot(mt, normal); | |
float length; | |
if(dot<=0 || 1/dot>mLimit) | |
length = width*mLimit; | |
else | |
length = width / dot; | |
mt = mt*length; | |
verts[di] = new Vector3(point.x + mt.x, point.y+mt.y, verts[di].z); | |
verts[di+1] = new Vector3(point.x - mt.x, point.y-mt.y, verts[di+1].z); | |
}else{ | |
normal = normal*width; | |
verts[di] = new Vector3(point.x+normal.x, point.y+normal.y, verts[di].z); | |
verts[di+1] = new Vector3(point.x-normal.x, point.y-normal.y, verts[di+1].z); | |
} | |
pDir = dir; | |
} | |
end = end-1; | |
var lDir = new Vector2(dir.y,-dir.x).normalized * width; | |
Vector2 lastPoint = points[end + 1]; | |
int lastI = end * 2 + 2; | |
verts[lastI] = new Vector3(lastPoint.x - lDir.x, lastPoint.y - lDir.y, verts[lastI].z); | |
verts[lastI+1] = new Vector3(lastPoint.x + lDir.x, lastPoint.y + lDir.y, verts[lastI+1].z); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment