├── .gitignore ├── Assets ├── Packages.meta └── Packages │ ├── Sketchbook.meta │ └── Sketchbook │ ├── Resources.meta │ ├── Resources │ ├── Shaders.meta │ └── Shaders │ │ ├── Sketchbook.shader │ │ └── Sketchbook.shader.meta │ ├── Scenes.meta │ ├── Scenes │ ├── BranchLines.unity │ ├── BranchLines.unity.meta │ ├── SnakePoints.unity │ ├── SnakePoints.unity.meta │ ├── TerrainNetwork.unity │ ├── TerrainNetwork.unity.meta │ ├── WarmBasket.unity │ └── WarmBasket.unity.meta │ ├── Scripts.meta │ └── Scripts │ ├── Demo.meta │ ├── Demo │ ├── BranchLines.cs │ ├── BranchLines.cs.meta │ ├── SnakePoints.cs │ ├── SnakePoints.cs.meta │ ├── TerrainNetwork.cs │ ├── TerrainNetwork.cs.meta │ ├── WarmBasket.cs │ └── WarmBasket.cs.meta │ ├── SketchbookBehaviour.cs │ └── SketchbookBehaviour.cs.meta ├── Captures └── WarmBasket.gif └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Pp]rojectSettings/ 6 | Papers/ 7 | 8 | # Autogenerated VS/MD solution and project files 9 | *.csproj 10 | *.unityproj 11 | *.sln 12 | *.suo 13 | *.user 14 | *.userprefs 15 | *.pidb 16 | *.booproj 17 | 18 | # Unity3D Generated File On Crash Reports 19 | sysinfo.txt -------------------------------------------------------------------------------- /Assets/Packages.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd8f51337c0899641816d6cff7a95c90 3 | folderAsset: yes 4 | timeCreated: 1479884381 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 222a59c66a1489449a708d0f96d960e9 3 | folderAsset: yes 4 | timeCreated: 1479884387 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 11617d4e822d0fd4ab4ef533f20ee616 3 | folderAsset: yes 4 | timeCreated: 1480059757 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Resources/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bc60864c81b62604ab4ae7d15006cc03 3 | folderAsset: yes 4 | timeCreated: 1479884397 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Resources/Shaders/Sketchbook.shader: -------------------------------------------------------------------------------- 1 | Shader "mattatz/Sketchbook" { 2 | 3 | Properties { 4 | _SrcBlend ("SrcBlend", Int) = 5 // SrcAlpha 5 | _DstBlend ("DstBlend", Int) = 10 // OneMinusSrcAlpha 6 | _ZWrite ("ZWrite", Int) = 1 // On 7 | _ZTest ("ZTest", Int) = 4 // LEqual 8 | _Cull ("Cull", Int) = 0 // Off 9 | _ZBias ("ZBias", Float) = 0.0 10 | } 11 | 12 | SubShader { 13 | Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } 14 | LOD 100 15 | 16 | Blend [_SrcBlend] [_DstBlend] 17 | ZWrite [_ZWrite] 18 | ZTest [_ZTest] 19 | // Cull [_Cull] 20 | Cull Back 21 | Offset [_ZBias], [_ZBias] 22 | 23 | CGINCLUDE 24 | 25 | #include "UnityCG.cginc" 26 | 27 | struct appdata { 28 | float4 vertex : POSITION; 29 | float2 uv : TEXCOORD0; 30 | float4 color : COLOR; 31 | }; 32 | 33 | ENDCG 34 | 35 | Pass { 36 | CGPROGRAM 37 | #pragma vertex vert 38 | #pragma fragment frag 39 | 40 | struct v2f { 41 | float4 vertex : SV_POSITION; 42 | float4 color : COLOR; 43 | }; 44 | 45 | v2f vert (appdata IN) { 46 | v2f OUT; 47 | OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex); 48 | OUT.color = IN.color; 49 | return OUT; 50 | } 51 | 52 | fixed4 frag (v2f IN) : SV_Target { 53 | return IN.color; 54 | } 55 | 56 | ENDCG 57 | } 58 | 59 | Pass { 60 | 61 | Tags { "LightMode"="ForwardBase" } 62 | 63 | CGPROGRAM 64 | 65 | #include "UnityLightingCommon.cginc" 66 | 67 | #pragma vertex vert 68 | #pragma geometry geom 69 | #pragma fragment frag 70 | 71 | struct v2g { 72 | float4 vertex : POSITION; 73 | float2 uv : TEXCOORD0; 74 | float4 color : COLOR; 75 | }; 76 | 77 | struct g2f { 78 | float4 pos : POSITION; 79 | float3 normal : NORMAL; 80 | float2 uv : TEXCOORD0; 81 | float4 color : COLOR; 82 | }; 83 | 84 | v2g vert (appdata IN) { 85 | v2g OUT; 86 | OUT.vertex = IN.vertex; 87 | OUT.uv = IN.uv; 88 | OUT.color = IN.color; 89 | return OUT; 90 | } 91 | 92 | [maxvertexcount(3)] 93 | void geom(triangle v2g IN[3], inout TriangleStream OUT) { 94 | float3 a = IN[0].vertex.xyz, b = IN[1].vertex.xyz, c = IN[2].vertex.xyz; 95 | float3 normal = cross(normalize(b - a), normalize(c - a)); 96 | 97 | for (int i = 0; i < 3; i++) { 98 | g2f pIn; 99 | pIn.pos = mul(UNITY_MATRIX_MVP, IN[i].vertex); 100 | pIn.uv = IN[i].uv; 101 | pIn.color = IN[i].color; 102 | pIn.normal = normal; 103 | OUT.Append(pIn); 104 | } 105 | } 106 | 107 | fixed4 frag (g2f IN) : SV_Target { 108 | float3 normal = normalize(IN.normal); 109 | normal = UnityObjectToWorldNormal(normal); 110 | 111 | // standard diffuse 112 | half nl = max(0, dot(normal, _WorldSpaceLightPos0.xyz)); 113 | half3 diff = nl * _LightColor0; 114 | // return fixed4(diff * (normal + 1.0) * 0.5, 1.0); 115 | 116 | IN.color.rgb *= diff; 117 | return IN.color; 118 | } 119 | 120 | ENDCG 121 | } 122 | 123 | 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Resources/Shaders/Sketchbook.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b825402dd2010248ad12e41eec5fcd7 3 | timeCreated: 1479885425 4 | licenseType: Pro 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9271c5eb32f050c48af98995ace7eb86 3 | folderAsset: yes 4 | timeCreated: 1479884406 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/BranchLines.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-sketchbook/c55612a948bc296506cd282f94d2540797e8a18a/Assets/Packages/Sketchbook/Scenes/BranchLines.unity -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/BranchLines.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ee52470ce7868749b0a025e8d8d0a11 3 | timeCreated: 1479884483 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/SnakePoints.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-sketchbook/c55612a948bc296506cd282f94d2540797e8a18a/Assets/Packages/Sketchbook/Scenes/SnakePoints.unity -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/SnakePoints.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 62ea02f50fc143842b0f3fbf06509663 3 | timeCreated: 1479884483 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/TerrainNetwork.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-sketchbook/c55612a948bc296506cd282f94d2540797e8a18a/Assets/Packages/Sketchbook/Scenes/TerrainNetwork.unity -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/TerrainNetwork.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70ec96a8f57a1d743890ad8c47d6863f 3 | timeCreated: 1479884483 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/WarmBasket.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-sketchbook/c55612a948bc296506cd282f94d2540797e8a18a/Assets/Packages/Sketchbook/Scenes/WarmBasket.unity -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scenes/WarmBasket.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e48b7fdce705beb45adcfc2f8db4036c 3 | timeCreated: 1479884483 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d1fc9222f9afd146b4306aaea6fbe4d 3 | folderAsset: yes 4 | timeCreated: 1479884393 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c88415b860c5144e8a7a13dcb07ef75 3 | folderAsset: yes 4 | timeCreated: 1480057819 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/BranchLines.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | namespace mattatz.Sketchbook { 7 | 8 | public class BranchLines : SketchbookBehaviour { 9 | 10 | Segment root; 11 | public int depth = 5; 12 | public int branches = 5; 13 | public Vector2 rotation = new Vector2(-40f, 40f); 14 | public Vector2 decay = new Vector2(0.75f, 0.9f); 15 | 16 | [Range(1f, 20f)] public float animationSpeed = 10f; 17 | 18 | protected override void Setup() { 19 | var q = Quaternion.AngleAxis(Random.Range(-5, 5f), Vector3.forward); 20 | root = new Segment(depth, transform.position, transform.position + q * Vector3.up * Random.Range(1f, 2f)); 21 | root.Grow(this); 22 | } 23 | 24 | void Update () { 25 | if(Input.GetMouseButtonDown(0)) { 26 | var q = Quaternion.AngleAxis(Random.Range(-5f, 5f), Vector3.forward); 27 | root = new Segment(depth, transform.position, transform.position + q * Vector3.up * Random.Range(1f, 2)); 28 | root.Grow(this); 29 | } 30 | 31 | root.Animate(Time.deltaTime * Mathf.Max(animationSpeed, 1f)); 32 | } 33 | 34 | protected override void Draw() { 35 | SetColor(Color.black); 36 | root.Draw(this); 37 | } 38 | 39 | public class Segment { 40 | public Vector3 from, to; 41 | public float t = 0f; 42 | List children; 43 | 44 | int depth; 45 | Vector3 cur; 46 | 47 | public Segment (int d, Vector3 p0, Vector3 p1) { 48 | children = new List(); 49 | 50 | depth = d; 51 | from = cur = p0; 52 | to = p1; 53 | t = 0f; 54 | } 55 | 56 | public virtual void Grow (BranchLines s) { 57 | if (depth <= 0) { 58 | children.Add(new Flower(0, to, to)); 59 | return; 60 | } 61 | 62 | if (depth <= 1) { 63 | var flowers = Random.Range(0, 2); 64 | for(int i = 0; i < flowers; i++) { 65 | children.Add(new Flower(0, Vector3.Lerp(from, to, Random.Range(0.75f, 0.9f)), to)); 66 | } 67 | } 68 | 69 | int count = Random.Range(1, s.branches); 70 | for(int i = 0; i < count; i++) { 71 | var q = Quaternion.AngleAxis(Random.Range(s.rotation.x, s.rotation.y), Vector3.forward); 72 | var segment = new Segment(depth - 1, to, to + q * (to - from) * Random.Range(s.decay.x, s.decay.y)); 73 | segment.Grow(s); 74 | children.Add(segment); 75 | } 76 | } 77 | 78 | public virtual void Animate (float dt) { 79 | if(t >= 1f) { 80 | children.ForEach(c => { 81 | c.Animate(dt); 82 | }); 83 | return; 84 | } 85 | 86 | t += dt; 87 | cur = Vector3.Lerp(from, to, Mathf.Clamp01(t)); 88 | } 89 | 90 | public virtual void Draw(BranchLines s) { 91 | s.SetColor(Color.black); 92 | s.DrawLine(from, cur); 93 | children.ForEach(c => c.Draw(s)); 94 | } 95 | } 96 | 97 | public class Flower : Segment { 98 | 99 | float offset; 100 | Color petal = new Color(1.0f, 0.72f, 0.8f); 101 | Color head = new Color(0.9f, 0.92f, 0.2f); 102 | 103 | public Flower (int d, Vector3 p0, Vector3 p1) : base(d, p0, p1) { 104 | offset = Random.Range(0f, TWO_PI); 105 | } 106 | 107 | public override void Grow(BranchLines s) {} 108 | 109 | public override void Animate(float dt) { 110 | t = Mathf.Clamp01(t + dt); 111 | } 112 | 113 | public override void Draw (BranchLines s) { 114 | 115 | s.SetColor(petal); 116 | 117 | for(int i = 0; i < 8; i++) { 118 | float r = (float)i / 8 * TWO_PI + offset; 119 | float x = Mathf.Cos(r), y = Mathf.Sin(r); 120 | var p = new Vector3(x, y, 0f) * 0.1f * t; 121 | s.DrawLine(from, from + p); 122 | } 123 | 124 | s.NoLights(); 125 | s.SetColor(head); 126 | s.DrawCircle(from, Quaternion.LookRotation(Vector3.forward), 0.025f * t); 127 | 128 | } 129 | 130 | } 131 | 132 | } 133 | 134 | } 135 | 136 | 137 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/BranchLines.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b55c93d4a4aa1d44b822ab10f7b162d3 3 | timeCreated: 1480057992 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: 8 | - shader: {fileID: 4800000, guid: 8b825402dd2010248ad12e41eec5fcd7, type: 3} 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/SnakePoints.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | namespace mattatz.Sketchbook { 7 | 8 | public class SnakePoints : SketchbookBehaviour { 9 | 10 | public List points; 11 | public Vector3 emitter = Vector3.zero; 12 | public Gradient grad; 13 | 14 | public float noiseSpeed = 1f; 15 | public float noiseScale = 5f; 16 | public float noiseIntensity = 10f; 17 | 18 | bool dragging; 19 | 20 | protected override void Setup() { 21 | points = new List(); 22 | } 23 | 24 | void Update () { 25 | if(!dragging) { 26 | var dt = Time.deltaTime; 27 | emitter += new Vector3( 28 | Mathf.PerlinNoise(Time.timeSinceLevelLoad, emitter.x) - 0.5f, 29 | Mathf.PerlinNoise(Time.timeSinceLevelLoad, emitter.y) - 0.5f, 30 | 0f 31 | ) * 10f * dt; 32 | dragging = Input.GetMouseButtonDown(0); 33 | } else { 34 | var world = Camera.main.ScreenToWorldPoint(Input.mousePosition); 35 | emitter.x = world.x; 36 | emitter.y = world.y; 37 | 38 | if(Input.GetMouseButtonUp(0)) { 39 | dragging = false; 40 | } 41 | } 42 | 43 | points.Add(new Point(emitter, grad.Evaluate(Random.value), Random.Range(0.02f, 0.2f), Random.Range(3f, 10f))); 44 | 45 | for(int i = points.Count - 1; i >= 0; i--) { 46 | var p = points[i]; 47 | p.Update(this, Time.deltaTime); 48 | if (p.lifetime <= 0f) points.RemoveAt(i); 49 | } 50 | 51 | } 52 | 53 | protected override void Draw() { 54 | 55 | NoLights(); 56 | 57 | points.ForEach(p => { 58 | p.Draw(this); 59 | }); 60 | 61 | } 62 | 63 | public class Point { 64 | public Vector3 position; 65 | public Vector3 velocity; 66 | public Color color; 67 | public float radius; 68 | public float lifetime; 69 | float length; 70 | 71 | public Point (Vector3 p, Color c, float r, float l) { 72 | position = p; 73 | color = c; 74 | radius = r; 75 | lifetime = length = l; 76 | } 77 | 78 | public void Update (SnakePoints s, float dt) { 79 | float t = Time.timeSinceLevelLoad * s.noiseSpeed; 80 | 81 | float n = (Mathf.PerlinNoise((position.x + t) * s.noiseScale, (position.y + t) * s.noiseScale) - 0.5f) * TWO_PI * 2f; 82 | velocity += new Vector3(Mathf.Cos(n), Mathf.Sin(n), 0f) * s.noiseIntensity * dt; 83 | velocity *= 0.9f; // decay 84 | lifetime -= dt; 85 | 86 | position += velocity; 87 | } 88 | 89 | public void Draw (SnakePoints s) { 90 | s.Fill(); 91 | s.SetColor(color); 92 | s.DrawCircle(position, Quaternion.LookRotation(Vector3.forward), radius * (lifetime / length)); 93 | 94 | s.NoFill(); 95 | s.SetColor(Color.black); 96 | s.DrawCircle(position, Quaternion.LookRotation(Vector3.forward), radius * (lifetime / length)); 97 | } 98 | } 99 | 100 | } 101 | 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/SnakePoints.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b9c4653715712474b8868e7483ef123e 3 | timeCreated: 1480057992 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: 8 | - shader: {fileID: 4800000, guid: 8b825402dd2010248ad12e41eec5fcd7, type: 3} 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/TerrainNetwork.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | namespace mattatz.Sketchbook { 7 | 8 | public class TerrainNetwork : SketchbookBehaviour { 9 | 10 | List nodes; 11 | public int width = 20; 12 | public int height = 20; 13 | public float unit = 0.5f; 14 | 15 | protected override void Setup() { 16 | nodes = new List(); 17 | 18 | var offsetX = - width * unit * 0.5f; 19 | var offsetY = - height * unit * 0.5f; 20 | 21 | for(int y = 0; y < height; y++) { 22 | for(int x = 0; x < width; x++) { 23 | var p = new Vector3(x * unit + offsetX, 0f, y * unit + offsetY); 24 | var n = new Node(p); 25 | nodes.Add(n); 26 | } 27 | } 28 | } 29 | 30 | void Update () { 31 | var dt = Time.deltaTime; 32 | nodes.ForEach(node => node.Update(dt)); 33 | } 34 | 35 | protected override void Draw() { 36 | SetColor(Color.black); 37 | nodes.ForEach(node => node.Draw(this)); 38 | } 39 | 40 | class Node { 41 | 42 | Vector3 position; 43 | Vector3 velocity; 44 | 45 | float offset; 46 | 47 | public Node (Vector3 p) { 48 | position = p; 49 | offset = Random.Range(0f, 100f); 50 | } 51 | 52 | public void Update (float dt) { 53 | } 54 | 55 | Quaternion look = Quaternion.LookRotation(Vector3.down, Vector3.back); 56 | public void Draw (TerrainNetwork s) { 57 | s.DrawCircle(position, look, 0.2f, 6); 58 | } 59 | 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/TerrainNetwork.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3611fc563102244c5a529eba8be815b2 3 | timeCreated: 1480249871 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/WarmBasket.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | namespace mattatz.Sketchbook { 7 | 8 | public class WarmBasket : SketchbookBehaviour { 9 | 10 | public float sphereRadius = 4.5f; 11 | public float rotationScale = 0.1f; 12 | public float rotationSpeed = 50f; 13 | public bool useGravity = false; 14 | 15 | List strokes; 16 | 17 | protected override void Setup() { 18 | strokes = new List(); 19 | 20 | for(int i = 0; i < 50; i++) { 21 | strokes.Add(new Warm(Random.insideUnitSphere * 0.25f, Color.black, 30)); 22 | } 23 | } 24 | 25 | void Update () { 26 | var dt = Time.deltaTime; 27 | 28 | strokes.ForEach(stroke => { 29 | stroke.Update(this, dt); 30 | }); 31 | 32 | var t = Time.timeSinceLevelLoad * rotationScale; 33 | var axis = new Vector3( 34 | Mathf.PerlinNoise(t, 0f) - 0.5f, 35 | Mathf.PerlinNoise(0f, t) - 0.5f, 36 | Mathf.PerlinNoise(t, t) - 0.5f 37 | ); 38 | transform.localRotation *= Quaternion.AngleAxis(Time.deltaTime * rotationSpeed, axis); 39 | 40 | if(!useGravity) { 41 | useGravity = Input.GetMouseButtonDown(0); 42 | } else { 43 | useGravity = !Input.GetMouseButtonUp(0); 44 | } 45 | } 46 | 47 | protected override void Draw() { 48 | 49 | strokes.ForEach(stroke => { 50 | stroke.Draw(this); 51 | }); 52 | 53 | SetColor(new Color(0.8f, 0.8f, 0.8f, 0.5f)); 54 | NoFill(); 55 | DrawSphere(Vector3.zero, Quaternion.LookRotation(Vector3.forward), sphereRadius, 10, 4); 56 | } 57 | 58 | public class Warm { 59 | 60 | List points; 61 | Color color; 62 | Vector3 velocity; 63 | 64 | float offset; 65 | float decay; 66 | float limit = 0.45f; 67 | 68 | public Warm (Vector3 p, Color c, int count) { 69 | color = c; 70 | points = new List(); 71 | for(int i = 0; i < count; i++) { 72 | points.Add(p); 73 | } 74 | 75 | offset = Random.Range(0f, 300f); 76 | decay = Random.Range(0.5f, 0.95f); 77 | } 78 | 79 | public void Update (WarmBasket s, float dt) { 80 | var head = points[0]; 81 | 82 | var t = Time.timeSinceLevelLoad * 0.25f; 83 | 84 | var nx = Mathf.PerlinNoise(t, head.x * 0.1f + offset) - 0.5f; 85 | var ny = Mathf.PerlinNoise(head.y * 0.1f + offset, t) - 0.5f; 86 | var nz = Mathf.PerlinNoise(t + offset + head.z * 0.1f, t + offset) - 0.5f; 87 | 88 | const float intensity = 1.0f; 89 | velocity.x += Mathf.Cos(nx * TWO_PI * 2f) * intensity; 90 | velocity.y += Mathf.Sin(ny * TWO_PI * 2f) * intensity; 91 | velocity.z += Mathf.Cos(nz * TWO_PI * 2f) * intensity; 92 | 93 | if(s.useGravity) { 94 | velocity += s.transform.InverseTransformDirection(Vector3.down) * 5f; 95 | } 96 | 97 | if(head.magnitude >= s.sphereRadius) { 98 | velocity += -head; 99 | } 100 | 101 | velocity *= decay; 102 | 103 | head += velocity * dt; 104 | head = head.normalized * Mathf.Clamp(head.magnitude, 0f, s.sphereRadius); 105 | 106 | points[0] = head; 107 | 108 | Chain(); 109 | } 110 | 111 | void Chain () { 112 | for(int i = 1, n = points.Count; i < n; i++) { 113 | var p0 = points[i - 1]; 114 | var p1 = points[i]; 115 | var dir = (p1 - p0); 116 | if(dir.magnitude > limit) { 117 | p1 = p0 + dir.normalized * limit; 118 | points[i] = p1; 119 | } 120 | } 121 | } 122 | 123 | Quaternion look = Quaternion.LookRotation(Vector3.forward); 124 | 125 | public void Draw (WarmBasket s) { 126 | s.SetColor(color); 127 | for(int i = 1, n = points.Count; i < n; i++) { 128 | s.DrawLine(points[i - 1], points[i]); 129 | } 130 | 131 | s.Fill(); 132 | 133 | float t = (Mathf.Sin(offset + Time.timeSinceLevelLoad * (decay * 5.0f)) + 1.0f) * 0.5f + 0.5f; 134 | t = Mathf.Clamp01(t); 135 | t = t * t; 136 | s.DrawSphere(points[0], look, limit * 0.25f * t); 137 | } 138 | 139 | } 140 | 141 | } 142 | 143 | } 144 | 145 | 146 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/Demo/WarmBasket.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 93eee992a014ecd45a9a17783f0a17d7 3 | timeCreated: 1480057992 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: 8 | - shader: {fileID: 4800000, guid: 8b825402dd2010248ad12e41eec5fcd7, type: 3} 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/SketchbookBehaviour.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Sketchbook { 9 | 10 | public abstract class SketchbookBehaviour : MonoBehaviour { 11 | 12 | public const string SHADER_PATH = "mattatz/Sketchbook"; 13 | public const float TWO_PI = Mathf.PI * 2f; 14 | 15 | Material material; 16 | 17 | #region Properties 18 | 19 | Color color = Color.white; 20 | bool fill = true; 21 | bool useLight = false; 22 | List matrices = new List() { Matrix4x4.identity }; 23 | 24 | #endregion 25 | 26 | #region Monobehaviour Lifecycle 27 | 28 | void Awake () { 29 | material = new Material(Shader.Find(SHADER_PATH)); 30 | Setup(); 31 | } 32 | 33 | void OnRenderObject () { 34 | if (material == null) return; 35 | Draw(); 36 | ClearMatrix(); 37 | } 38 | 39 | #endregion 40 | 41 | #region Sketchbook Lifecycle 42 | 43 | protected virtual void Setup () {} 44 | protected virtual void Draw () {} 45 | 46 | #endregion 47 | 48 | #region Color 49 | 50 | public void SetColor (Color c) { 51 | this.color = c; 52 | } 53 | 54 | public void Fill () { 55 | fill = true; 56 | } 57 | 58 | public void NoFill () { 59 | fill = false; 60 | } 61 | 62 | public void Lights () { 63 | useLight = true; 64 | } 65 | 66 | public void NoLights () { 67 | useLight = false; 68 | } 69 | 70 | #endregion 71 | 72 | #region Matrix 73 | 74 | public void PushMatrix () { 75 | matrices.Add(Matrix4x4.identity); 76 | } 77 | 78 | public void PopMatrix () { 79 | if(matrices.Count > 0) { 80 | matrices.RemoveAt(matrices.Count - 1); 81 | } 82 | } 83 | 84 | Matrix4x4 CurrentMatrix () { 85 | if(matrices.Count > 0) { 86 | return matrices.Last(); 87 | } 88 | return Matrix4x4.identity; 89 | } 90 | 91 | void ClearMatrix () { 92 | matrices.Clear(); 93 | matrices.Add(Matrix4x4.identity); 94 | } 95 | 96 | public void Translate (Vector3 position) { 97 | int n = matrices.Count; 98 | if(n > 0) { 99 | matrices[n - 1] = matrices[n - 1] * Matrix4x4.TRS(position, Quaternion.identity, Vector3.one); 100 | } 101 | } 102 | 103 | public void Rotate (Quaternion rotation) { 104 | int n = matrices.Count; 105 | if(n > 0) { 106 | matrices[n - 1] = matrices[n - 1] * Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one); 107 | } 108 | } 109 | 110 | public void Scale (Vector3 scale) { 111 | int n = matrices.Count; 112 | if(n > 0) { 113 | matrices[n - 1] = matrices[n - 1] * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, scale); 114 | } 115 | } 116 | 117 | #endregion 118 | 119 | #region Draw 120 | 121 | void Draw (int mode, Matrix4x4 model, Action draw) { 122 | if(mode == GL.TRIANGLES && useLight) { 123 | material.SetPass(1); 124 | } else { 125 | material.SetPass(0); 126 | } 127 | 128 | GL.PushMatrix(); 129 | 130 | /* 131 | GL.LoadIdentity(); 132 | GL.MultMatrix(Camera.current.cameraToWorldMatrix * CurrentMatrix() * model); 133 | */ 134 | 135 | GL.MultMatrix(transform.localToWorldMatrix * CurrentMatrix() * model); 136 | 137 | GL.Begin(mode); 138 | GL.Color(color); 139 | draw(); 140 | GL.End(); 141 | 142 | GL.PopMatrix(); 143 | } 144 | 145 | public void DrawLine (Vector3 from, Vector3 to) { 146 | Draw(GL.LINES, Matrix4x4.identity, () => { 147 | GL.Vertex(from); 148 | GL.Vertex(to); 149 | }); 150 | } 151 | 152 | public void DrawTriangle (Vector3 a, Vector3 b, Vector3 c) { 153 | if(fill) { 154 | Draw(GL.LINES, Matrix4x4.identity, () => { 155 | GL.Vertex(a); GL.Vertex(b); 156 | GL.Vertex(b); GL.Vertex(c); 157 | GL.Vertex(c); GL.Vertex(a); 158 | }); 159 | } else { 160 | Draw(GL.TRIANGLES, Matrix4x4.identity, () => { 161 | GL.Vertex(a); 162 | GL.Vertex(b); 163 | GL.Vertex(c); 164 | }); 165 | } 166 | } 167 | 168 | public void DrawRect (Vector3 center, Quaternion look, Vector2 size) { 169 | var mat = Matrix4x4.TRS(center, look, Vector3.one); 170 | var hx = size.x * 0.5f; 171 | var hy = size.y * 0.5f; 172 | 173 | if(fill) { 174 | Draw(GL.TRIANGLES, mat, () => { 175 | GL.Vertex(new Vector3(-hx, -hy, 0f)); 176 | GL.Vertex(new Vector3( hx, hy, 0f)); 177 | GL.Vertex(new Vector3( hx, -hy, 0f)); 178 | 179 | GL.Vertex(new Vector3(-hx, -hy, 0f)); 180 | GL.Vertex(new Vector3(-hx, hy, 0f)); 181 | GL.Vertex(new Vector3( hx, hy, 0f)); 182 | }); 183 | } else { 184 | Draw(GL.LINES, mat, () => { 185 | // bottom 186 | GL.Vertex(new Vector3(-hx, -hy, 0f)); 187 | GL.Vertex(new Vector3( hx, -hy, 0f)); 188 | 189 | // right 190 | GL.Vertex(new Vector3( hx, -hy, 0f)); 191 | GL.Vertex(new Vector3( hx, hy, 0f)); 192 | 193 | // top 194 | GL.Vertex(new Vector3( hx, hy, 0f)); 195 | GL.Vertex(new Vector3(-hx, hy, 0f)); 196 | 197 | // left 198 | GL.Vertex(new Vector3(-hx, hy, 0f)); 199 | GL.Vertex(new Vector3(-hx, -hy, 0f)); 200 | }); 201 | } 202 | } 203 | 204 | public void DrawCircle (Vector3 center, Quaternion look, float radius, int resolution = 36) { 205 | var mat = Matrix4x4.TRS(center, look, Vector3.one); 206 | 207 | float step = TWO_PI / resolution; 208 | 209 | if(fill) { 210 | Draw(GL.TRIANGLES, mat, () => { 211 | for(float t = 0f; t <= TWO_PI; t += step) { 212 | var x1 = Mathf.Cos(t) * radius; 213 | var x2 = Mathf.Cos(t + step) * radius; 214 | var y1 = Mathf.Sin(t) * radius; 215 | var y2 = Mathf.Sin(t + step) * radius; 216 | GL.Vertex3(x1, y1, 0f); 217 | GL.Vertex3(0f, 0f, 0f); 218 | GL.Vertex3(x2, y2, 0f); 219 | } 220 | }); 221 | } else { 222 | Draw(GL.LINES, mat, () => { 223 | for(float t = 0f; t <= TWO_PI; t += step) { 224 | var x1 = Mathf.Cos(t) * radius; 225 | var x2 = Mathf.Cos(t + step) * radius; 226 | var y1 = Mathf.Sin(t) * radius; 227 | var y2 = Mathf.Sin(t + step) * radius; 228 | GL.Vertex3(x1, y1, 0f); 229 | GL.Vertex3(x2, y2, 0f); 230 | } 231 | }); 232 | } 233 | } 234 | 235 | Vector3[] corners = new Vector3[] { 236 | // bottom 237 | new Vector3(-0.5f, -0.5f, -0.5f), 238 | new Vector3( 0.5f, -0.5f, -0.5f), 239 | new Vector3( 0.5f, -0.5f, 0.5f), 240 | new Vector3(-0.5f, -0.5f, 0.5f), 241 | 242 | // top 243 | new Vector3(-0.5f, 0.5f, -0.5f), 244 | new Vector3( 0.5f, 0.5f, -0.5f), 245 | new Vector3( 0.5f, 0.5f, 0.5f), 246 | new Vector3(-0.5f, 0.5f, 0.5f), 247 | }; 248 | 249 | public void DrawBox (Vector3 center, Quaternion look, Vector3 size) { 250 | if(fill) { 251 | Draw(GL.TRIANGLES, Matrix4x4.TRS(center, look, size), () => { 252 | // bottom 253 | GL.Vertex(corners[0]); 254 | GL.Vertex(corners[2]); 255 | GL.Vertex(corners[3]); 256 | 257 | GL.Vertex(corners[0]); 258 | GL.Vertex(corners[1]); 259 | GL.Vertex(corners[2]); 260 | 261 | // top 262 | GL.Vertex(corners[4]); 263 | GL.Vertex(corners[7]); 264 | GL.Vertex(corners[6]); 265 | 266 | GL.Vertex(corners[4]); 267 | GL.Vertex(corners[6]); 268 | GL.Vertex(corners[5]); 269 | 270 | // front 271 | GL.Vertex(corners[0]); 272 | GL.Vertex(corners[4]); 273 | GL.Vertex(corners[5]); 274 | 275 | GL.Vertex(corners[0]); 276 | GL.Vertex(corners[5]); 277 | GL.Vertex(corners[1]); 278 | 279 | // back 280 | GL.Vertex(corners[3]); 281 | GL.Vertex(corners[6]); 282 | GL.Vertex(corners[7]); 283 | 284 | GL.Vertex(corners[2]); 285 | GL.Vertex(corners[6]); 286 | GL.Vertex(corners[3]); 287 | 288 | // left 289 | GL.Vertex(corners[0]); 290 | GL.Vertex(corners[3]); 291 | GL.Vertex(corners[7]); 292 | 293 | GL.Vertex(corners[0]); 294 | GL.Vertex(corners[7]); 295 | GL.Vertex(corners[4]); 296 | 297 | // right 298 | GL.Vertex(corners[2]); 299 | GL.Vertex(corners[1]); 300 | GL.Vertex(corners[5]); 301 | 302 | GL.Vertex(corners[2]); 303 | GL.Vertex(corners[5]); 304 | GL.Vertex(corners[6]); 305 | }); 306 | } else { 307 | Draw(GL.LINES, Matrix4x4.TRS(center, look, size), () => { 308 | // bottom 309 | GL.Vertex(corners[0]); 310 | GL.Vertex(corners[1]); 311 | GL.Vertex(corners[1]); 312 | GL.Vertex(corners[2]); 313 | GL.Vertex(corners[2]); 314 | GL.Vertex(corners[3]); 315 | GL.Vertex(corners[3]); 316 | GL.Vertex(corners[0]); 317 | 318 | // top 319 | GL.Vertex(corners[4]); 320 | GL.Vertex(corners[5]); 321 | GL.Vertex(corners[5]); 322 | GL.Vertex(corners[6]); 323 | GL.Vertex(corners[6]); 324 | GL.Vertex(corners[7]); 325 | GL.Vertex(corners[7]); 326 | GL.Vertex(corners[4]); 327 | 328 | // side 329 | GL.Vertex(corners[0]); 330 | GL.Vertex(corners[4]); 331 | GL.Vertex(corners[1]); 332 | GL.Vertex(corners[5]); 333 | GL.Vertex(corners[2]); 334 | GL.Vertex(corners[6]); 335 | GL.Vertex(corners[3]); 336 | GL.Vertex(corners[7]); 337 | }); 338 | } 339 | 340 | } 341 | 342 | public void DrawSphere (Vector3 center, Quaternion look, float radius, int resolution = 16) { 343 | DrawSphere(center, look, radius, resolution, resolution); 344 | } 345 | 346 | public void DrawSphere (Vector3 center, Quaternion look, float radius, int uresolution, int vresolution) { 347 | int len = (uresolution + 1) * vresolution + 2; 348 | 349 | var vertices = new Vector3[len]; 350 | vertices[0] = Vector3.up * radius; 351 | for (int v = 0; v < vresolution; v++) { 352 | float a1 = Mathf.PI * (float)(v + 1) / (vresolution + 1); 353 | float sin = Mathf.Sin(a1); 354 | float cos = Mathf.Cos(a1); 355 | for (int u = 0; u <= uresolution; u++) { 356 | float a2 = TWO_PI * (float)(u == uresolution ? 0 : u) / uresolution; 357 | float sin2 = Mathf.Sin(a2); 358 | float cos2 = Mathf.Cos(a2); 359 | vertices[u + v * (uresolution + 1) + 1] = new Vector3(sin * cos2, cos, sin * sin2) * radius; 360 | } 361 | } 362 | vertices[vertices.Length - 1] = Vector3.up * -radius; 363 | 364 | var mat = Matrix4x4.TRS(center, look, Vector3.one); 365 | 366 | if(fill) { 367 | Draw(GL.TRIANGLES, mat, () => { 368 | // top cap 369 | for (int u = 0; u < uresolution; u++) { 370 | GL.Vertex(vertices[u + 2]); 371 | GL.Vertex(vertices[u + 1]); 372 | GL.Vertex(vertices[0]); 373 | } 374 | // middle 375 | for (int v = 0; v < vresolution - 1; v++) { 376 | for (int u = 0; u < uresolution; u++) { 377 | int current = u + v * (uresolution + 1) + 1; 378 | int next = current + uresolution + 1; 379 | 380 | GL.Vertex(vertices[current]); 381 | GL.Vertex(vertices[current + 1]); 382 | GL.Vertex(vertices[next + 1]); 383 | 384 | GL.Vertex(vertices[current]); 385 | GL.Vertex(vertices[next + 1]); 386 | GL.Vertex(vertices[next]); 387 | } 388 | } 389 | // bottom cap 390 | for (int u = 0; u < uresolution; u++) { 391 | GL.Vertex(vertices[len - 1]); 392 | GL.Vertex(vertices[len - (u + 2) - 1]); 393 | GL.Vertex(vertices[len - (u + 1) - 1]); 394 | } 395 | }); 396 | } else { 397 | Draw(GL.LINES, mat, () => { 398 | // top cap 399 | for (int u = 0; u < uresolution; u++) { 400 | GL.Vertex(vertices[0]); 401 | GL.Vertex(vertices[u + 1]); 402 | } 403 | // middle 404 | for (int v = 0; v < vresolution - 1; v++) { 405 | for (int u = 0; u < uresolution; u++) { 406 | int current = u + v * (uresolution + 1) + 1; 407 | int next = current + uresolution + 1; 408 | 409 | GL.Vertex(vertices[current]); 410 | GL.Vertex(vertices[current + 1]); 411 | 412 | GL.Vertex(vertices[next + 1]); 413 | GL.Vertex(vertices[next]); 414 | 415 | GL.Vertex(vertices[current]); 416 | GL.Vertex(vertices[next]); 417 | } 418 | } 419 | // bottom cap 420 | for (int u = 0; u < uresolution; u++) { 421 | GL.Vertex(vertices[len - 1]); 422 | GL.Vertex(vertices[len - (u + 1) - 1]); 423 | } 424 | }); 425 | } 426 | 427 | } 428 | 429 | #endregion 430 | 431 | } 432 | 433 | } 434 | 435 | 436 | -------------------------------------------------------------------------------- /Assets/Packages/Sketchbook/Scripts/SketchbookBehaviour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ae27f8822244bcf4fb42337d12c2b1f1 3 | timeCreated: 1480057972 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: 8 | - shader: {fileID: 4800000, guid: 8b825402dd2010248ad12e41eec5fcd7, type: 3} 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Captures/WarmBasket.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-sketchbook/c55612a948bc296506cd282f94d2540797e8a18a/Captures/WarmBasket.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | unity-sketchbook 2 | ===================== 3 | 4 | Sketching utility like [Processing](https://processing.org/) for Unity. 5 | 6 | This library makes it possible to draw some shapes easily by [GL API](https://docs.unity3d.com/ScriptReference/GL.html). 7 | 8 | ## Examples 9 | 10 | now writing... 11 | 12 | ## Usage 13 | 14 | now writing... 15 | 16 | ## Sources 17 | 18 | - Processing - https://processing.org/ 19 | - nobnak/Gist GLFigure.cs - https://github.com/nobnak/Gist/blob/master/GLFigure.cs 20 | --------------------------------------------------------------------------------