├── ProjectSettings ├── ProjectVersion.txt ├── AudioManager.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── TagManager.asset ├── TimeManager.asset ├── EditorSettings.asset ├── NavMeshLayers.asset ├── NetworkManager.asset ├── DynamicsManager.asset ├── GraphicsSettings.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── QualitySettings.asset ├── UnityAdsSettings.asset ├── ClusterInputManager.asset ├── EditorBuildSettings.asset └── UnityConnectSettings.asset ├── Assets ├── Materials │ ├── Red.mat │ ├── Leaf.mat │ ├── Wood.mat │ ├── Leaf.mat.meta │ ├── Wood.mat.meta │ └── Red.mat.meta ├── Models │ ├── leaf.FBX │ └── leaf.FBX.meta ├── Scenes │ ├── Main.unity │ └── Main.unity.meta ├── Textures │ ├── Leaf.png │ ├── leaf.jpg │ ├── tree-trunk.jpg │ ├── Leaf.png.meta │ ├── leaf.jpg.meta │ └── tree-trunk.jpg.meta ├── Prefabs │ ├── Leaf.prefab │ └── Leaf.prefab.meta ├── Defs.meta ├── Data │ ├── Tree A.txt │ ├── Kosh Snowflake.txt │ ├── 3D Tree 1.txt │ ├── Tree A.txt.meta │ ├── 3D Tree 1.txt.meta │ └── Kosh Snowflake.txt.meta ├── Defs │ ├── Tree A.txt │ ├── Kosh Snowflake.txt │ ├── 3D Tree 1.txt │ ├── Tree A.txt.meta │ ├── 3D Tree 1.txt.meta │ └── Kosh Snowflake.txt.meta ├── Materials.meta ├── Models.meta ├── Prefabs.meta ├── Scenes.meta ├── Scripts.meta ├── Shaders.meta ├── Textures.meta ├── Shaders │ ├── CullOffDiffuse.shader.meta │ ├── CullOffCutoutDiffuse.shader.meta │ ├── CullOffDiffuse.shader │ └── CullOffCutoutDiffuse.shader ├── Data.meta └── Scripts │ ├── ProceduralMeshes.cs.meta │ ├── Production.cs.meta │ ├── LSystemExecutor.cs.meta │ ├── ProductionMatcher.cs.meta │ ├── LSystemDeriver.cs.meta │ ├── LSystemParser.cs.meta │ ├── LSystemInterpreter.cs.meta │ ├── Production.cs │ ├── LSystemDeriver.cs │ ├── ProductionMatcher.cs │ ├── LSystemParser.cs │ ├── LSystemExecutor.cs │ ├── LSystemInterpreter.cs │ └── ProceduralMeshes.cs ├── .gitignore ├── LICENCE.txt └── README.md /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.3.5f1 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /Assets/Materials/Red.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Materials/Red.mat -------------------------------------------------------------------------------- /Assets/Models/leaf.FBX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Models/leaf.FBX -------------------------------------------------------------------------------- /Assets/Scenes/Main.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Scenes/Main.unity -------------------------------------------------------------------------------- /Assets/Textures/Leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Textures/Leaf.png -------------------------------------------------------------------------------- /Assets/Textures/leaf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Textures/leaf.jpg -------------------------------------------------------------------------------- /Assets/Materials/Leaf.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Materials/Leaf.mat -------------------------------------------------------------------------------- /Assets/Materials/Wood.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Materials/Wood.mat -------------------------------------------------------------------------------- /Assets/Prefabs/Leaf.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Prefabs/Leaf.prefab -------------------------------------------------------------------------------- /Assets/Defs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3556717d58452c04fba5e09f19ef7291 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Textures/tree-trunk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/Assets/Textures/tree-trunk.jpg -------------------------------------------------------------------------------- /Assets/Data/Tree A.txt: -------------------------------------------------------------------------------- 1 | // tree A (pg. 25) 2 | 3 | axiom=F 4 | angle=25.7 5 | number of derivations=3 6 | 7 | F=(1)F[+F]F[-F]F -------------------------------------------------------------------------------- /Assets/Defs/Tree A.txt: -------------------------------------------------------------------------------- 1 | // tree A (pg. 25) 2 | 3 | axiom=F 4 | angle=25.7 5 | number of derivations=4 6 | 7 | F=(1)F[+F]F[-F]F -------------------------------------------------------------------------------- /Assets/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33e3eabbbe041814390a5c77e8462c2d 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Models.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5114e1ae210cef141897d075ac5d1cc2 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa6eec5b51c6ffa41b39bae26e96778e 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e7052762db088cd4eaf910e01a5b853f 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 888597cf9fcea09469ad03febe1eda8a 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: af51bb86937dec14180282b7f99cc30a 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Textures.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79fe1c1c255731a4393071ec8058ac10 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshLayers.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/NavMeshLayers.asset -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /Assets/Data/Kosh Snowflake.txt: -------------------------------------------------------------------------------- 1 | // kosh snowflake 2 | 3 | axiom=F--F--F 4 | angle=60 5 | number of derivations=3 6 | 7 | F=(1)F+F--F+F -------------------------------------------------------------------------------- /Assets/Defs/Kosh Snowflake.txt: -------------------------------------------------------------------------------- 1 | // kosh snowflake 2 | 3 | axiom=F--F--F 4 | angle=60 5 | number of derivations=3 6 | 7 | F=(1)F+F--F+F -------------------------------------------------------------------------------- /Assets/Scenes/Main.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 224ff1a6ae29b9e46baa20a12b4fea6f 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /Assets/Materials/Leaf.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 59ca09ba21dc9e147874e4ae91ad4c4d 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Materials/Wood.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0111e60b9b801f4489bc38340dd44014 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Prefabs/Leaf.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 80322d092fba782479c51099b7672c3d 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /Assets/Data/3D Tree 1.txt: -------------------------------------------------------------------------------- 1 | // 3D Tree 1 2 | 3 | axiom=F 4 | angle=22.5 5 | number of derivations=3 6 | 7 | F=(1)F[-&^F][^++&F]||F[--&^F][+&F] 8 | -------------------------------------------------------------------------------- /Assets/Defs/3D Tree 1.txt: -------------------------------------------------------------------------------- 1 | // 3D Tree 1 2 | 3 | axiom=F 4 | angle=22.5 5 | number of derivations=3 6 | 7 | F=(1)F[-&^F][^++&F]||F[--&^F][+&F] 8 | -------------------------------------------------------------------------------- /Assets/Shaders/CullOffDiffuse.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f6ed873f1d06144a970da74a5c7fcce 3 | ShaderImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboechat/LSystemsInUnity/HEAD/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /Assets/Shaders/CullOffCutoutDiffuse.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 104c5f145f710a646bd6717608dace3d 3 | ShaderImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | 5 | # Autogenerated VS/MD solution and project files 6 | *.csproj 7 | *.unityproj 8 | *.suo 9 | *.sln 10 | *.pidb 11 | *.userprefs 12 | -------------------------------------------------------------------------------- /Assets/Data.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e3ac3eb4492b4c45b48ab3310412729 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/Data/Tree A.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 947b8f0974642ad4d8c908fb9d926c28 3 | timeCreated: 1469653201 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Defs/Tree A.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 363958e11ee794a4cb28a14256b8d266 3 | timeCreated: 1469653201 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Data/3D Tree 1.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9493ad4fb1f527348a21b5821cf663ea 3 | timeCreated: 1469653201 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Defs/3D Tree 1.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dcfcd583831ca7f4b83a1d3e686e7359 3 | timeCreated: 1469653201 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Materials/Red.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 75c115512c3ea4a4499fe1b049ad9d2a 3 | timeCreated: 1469653720 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Data/Kosh Snowflake.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78e3f66268225fe4e9b9705203abc46a 3 | timeCreated: 1469653201 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Defs/Kosh Snowflake.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 89d1be1dc916e0047b7cafcb565c5bbb 3 | timeCreated: 1469653201 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scripts/ProceduralMeshes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99cb46cd457f7d94aa9fa9bb0a26d400 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Scripts/Production.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cddd81d7a5f6b9f4fa1ea8ff4dfb3225 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Scripts/LSystemExecutor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 102799ea13e1173498c8d667e61f6cd2 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Scripts/ProductionMatcher.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 64c214d22563f534aad340525124cb47 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Scripts/LSystemDeriver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a200cb2c0f1bf0c46baf5ecd1c5dfb93 3 | timeCreated: 1469479926 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/Scripts/LSystemParser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e16471e8a5660b4f923f0ae7ce2c49d 3 | timeCreated: 1469650430 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/Scripts/LSystemInterpreter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4fd656ff6f84846468c12ac8c99b12a3 3 | timeCreated: 1469652906 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/Shaders/CullOffDiffuse.shader: -------------------------------------------------------------------------------- 1 | Shader "Cull-Off Diffuse" { 2 | Properties { 3 | _Color ("Main Color", Color) = (1,1,1,1) 4 | _MainTex ("Base (RGB)", 2D) = "white" {} 5 | } 6 | 7 | SubShader { 8 | Tags { "RenderType"="Opaque" } 9 | LOD 200 10 | Cull Off 11 | 12 | CGPROGRAM 13 | #pragma surface surf Lambert 14 | 15 | sampler2D _MainTex; 16 | fixed4 _Color; 17 | 18 | struct Input { 19 | float2 uv_MainTex; 20 | }; 21 | 22 | void surf (Input IN, inout SurfaceOutput o) { 23 | fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; 24 | o.Albedo = c.rgb; 25 | o.Alpha = c.a; 26 | } 27 | ENDCG 28 | } 29 | 30 | Fallback "VertexLit" 31 | } -------------------------------------------------------------------------------- /Assets/Shaders/CullOffCutoutDiffuse.shader: -------------------------------------------------------------------------------- 1 | Shader "Cull-Off Cutout" { 2 | Properties { 3 | _Color ("Main Color", Color) = (1,1,1,1) 4 | _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {} 5 | _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 6 | } 7 | 8 | SubShader { 9 | Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"} 10 | LOD 200 11 | Cull Off 12 | 13 | CGPROGRAM 14 | #pragma surface surf Lambert alphatest:_Cutoff 15 | 16 | sampler2D _MainTex; 17 | fixed4 _Color; 18 | 19 | struct Input { 20 | float2 uv_MainTex; 21 | }; 22 | 23 | void surf (Input IN, inout SurfaceOutput o) { 24 | fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; 25 | o.Albedo = c.rgb; 26 | o.Alpha = c.a; 27 | } 28 | ENDCG 29 | } 30 | 31 | Fallback "Transparent/Cutout/VertexLit" 32 | } 33 | -------------------------------------------------------------------------------- /Assets/Scripts/Production.cs: -------------------------------------------------------------------------------- 1 | public class Production 2 | { 3 | private string _predecessor; 4 | private string _successor; 5 | private float _probability; 6 | 7 | public string predecessor 8 | { 9 | get 10 | { 11 | return this._predecessor; 12 | } 13 | } 14 | 15 | public float probability 16 | { 17 | get 18 | { 19 | return this._probability; 20 | } 21 | } 22 | 23 | public string successor 24 | { 25 | get 26 | { 27 | return this._successor; 28 | } 29 | } 30 | 31 | public Production(string predecessor, string sucessor, float probability) 32 | { 33 | _predecessor = predecessor; 34 | _successor = sucessor; 35 | _probability = probability; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /Assets/Textures/Leaf.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 56149a4d0580ecd4599d74868d59954b 3 | TextureImporter: 4 | serializedVersion: 2 5 | mipmaps: 6 | mipMapMode: 0 7 | enableMipMap: 1 8 | linearTexture: 0 9 | correctGamma: 0 10 | fadeOut: 0 11 | borderMipMap: 0 12 | mipMapFadeDistanceStart: 1 13 | mipMapFadeDistanceEnd: 3 14 | bumpmap: 15 | convertToNormalMap: 0 16 | externalNormalMap: 0 17 | heightScale: .25 18 | normalMapFilter: 0 19 | isReadable: 0 20 | grayScaleToAlpha: 0 21 | generateCubemap: 0 22 | seamlessCubemap: 0 23 | textureFormat: -1 24 | maxTextureSize: 1024 25 | textureSettings: 26 | filterMode: -1 27 | aniso: -1 28 | mipBias: -1 29 | wrapMode: -1 30 | nPOTScale: 1 31 | lightmap: 0 32 | compressionQuality: 50 33 | textureType: -1 34 | buildTargetSettings: [] 35 | userData: 36 | -------------------------------------------------------------------------------- /Assets/Textures/leaf.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b36674f3c1907d4e81809164c837564 3 | TextureImporter: 4 | serializedVersion: 2 5 | mipmaps: 6 | mipMapMode: 0 7 | enableMipMap: 1 8 | linearTexture: 0 9 | correctGamma: 0 10 | fadeOut: 0 11 | borderMipMap: 0 12 | mipMapFadeDistanceStart: 1 13 | mipMapFadeDistanceEnd: 3 14 | bumpmap: 15 | convertToNormalMap: 0 16 | externalNormalMap: 0 17 | heightScale: .25 18 | normalMapFilter: 0 19 | isReadable: 0 20 | grayScaleToAlpha: 0 21 | generateCubemap: 0 22 | seamlessCubemap: 0 23 | textureFormat: -1 24 | maxTextureSize: 1024 25 | textureSettings: 26 | filterMode: -1 27 | aniso: -1 28 | mipBias: -1 29 | wrapMode: -1 30 | nPOTScale: 1 31 | lightmap: 0 32 | compressionQuality: 50 33 | textureType: -1 34 | buildTargetSettings: [] 35 | userData: 36 | -------------------------------------------------------------------------------- /Assets/Textures/tree-trunk.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40529e37ab09c1c4cb2e49b25f6ed837 3 | TextureImporter: 4 | serializedVersion: 2 5 | mipmaps: 6 | mipMapMode: 0 7 | enableMipMap: 1 8 | linearTexture: 0 9 | correctGamma: 0 10 | fadeOut: 0 11 | borderMipMap: 0 12 | mipMapFadeDistanceStart: 1 13 | mipMapFadeDistanceEnd: 3 14 | bumpmap: 15 | convertToNormalMap: 0 16 | externalNormalMap: 0 17 | heightScale: .25 18 | normalMapFilter: 0 19 | isReadable: 0 20 | grayScaleToAlpha: 0 21 | generateCubemap: 0 22 | seamlessCubemap: 0 23 | textureFormat: -1 24 | maxTextureSize: 1024 25 | textureSettings: 26 | filterMode: -1 27 | aniso: -1 28 | mipBias: -1 29 | wrapMode: -1 30 | nPOTScale: 1 31 | lightmap: 0 32 | compressionQuality: 50 33 | textureType: -1 34 | buildTargetSettings: [] 35 | userData: 36 | -------------------------------------------------------------------------------- /Assets/Scripts/LSystemDeriver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public static class LSystemDeriver 5 | { 6 | public static void Derive(string axiom, float angle, int derivations, Dictionary> productions, out string moduleString) 7 | { 8 | moduleString = axiom; 9 | for (int i = 0; i < Math.Max(1, derivations); i++) 10 | { 11 | string newModuleString = ""; 12 | for (int j = 0; j < moduleString.Length; j++) 13 | { 14 | string module = moduleString[j] + ""; 15 | if (!productions.ContainsKey(module)) 16 | { 17 | newModuleString += module; 18 | continue; 19 | } 20 | var production = ProductionMatcher.Match(module, productions); 21 | newModuleString += production.successor; 22 | } 23 | moduleString = newModuleString; 24 | } 25 | } 26 | 27 | } 28 | 29 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Assets/Scripts/ProductionMatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public static class ProductionMatcher 5 | { 6 | public static Production Match(string module, Dictionary> productions) 7 | { 8 | if (!productions.ContainsKey(module)) 9 | return null; 10 | List matches = productions[module]; 11 | if (matches.Count == 1) 12 | return matches[0]; 13 | float chance = UnityEngine.Random.value, accProbability = 0; 14 | foreach (var match in matches) 15 | { 16 | accProbability += match.probability; 17 | if (accProbability <= chance) 18 | return match; 19 | } 20 | // TODO: throw an assertion! 21 | throw new Exception("Should never happen!"); 22 | } 23 | 24 | public static bool CheckProbabilities(Dictionary> productions) 25 | { 26 | foreach (var matches in productions.Values) 27 | { 28 | if (matches.Count == 1 && matches[0].probability != 1) 29 | return false; 30 | float accProbabilities = 0; 31 | foreach (var match in matches) 32 | accProbabilities += match.probability; 33 | if (accProbabilities != 1) 34 | return false; 35 | } 36 | return true; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /Assets/Models/leaf.FBX.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7ddec1bfbcb358947b66149e283e3b2d 3 | ModelImporter: 4 | serializedVersion: 15 5 | fileIDToRecycleName: 6 | 100000: //RootNode 7 | 400000: //RootNode 8 | 2300000: //RootNode 9 | 3300000: //RootNode 10 | 4300000: level 11 | 9500000: //RootNode 12 | materials: 13 | importMaterials: 1 14 | materialName: 0 15 | materialSearch: 1 16 | animations: 17 | legacyGenerateAnimations: 4 18 | bakeSimulation: 0 19 | animationCompression: 1 20 | animationRotationError: .5 21 | animationPositionError: .5 22 | animationScaleError: .5 23 | animationWrapMode: 0 24 | clipAnimations: [] 25 | isReadable: 1 26 | meshes: 27 | lODScreenPercentages: [] 28 | globalScale: 2 29 | meshCompression: 0 30 | addColliders: 0 31 | swapUVChannels: 0 32 | generateSecondaryUV: 0 33 | useFileUnits: 1 34 | optimizeMeshForGPU: 1 35 | secondaryUVAngleDistortion: 8 36 | secondaryUVAreaDistortion: 15.000001 37 | secondaryUVHardAngle: 88 38 | secondaryUVPackMargin: 4 39 | tangentSpace: 40 | normalSmoothAngle: 60 41 | splitTangentsAcrossUV: 1 42 | normalImportMode: 0 43 | tangentImportMode: 1 44 | importAnimation: 1 45 | copyAvatar: 0 46 | humanDescription: 47 | human: [] 48 | skeleton: [] 49 | handles: [] 50 | armTwist: .5 51 | foreArmTwist: .5 52 | upperLegTwist: .5 53 | legTwist: .5 54 | armStretch: .0500000007 55 | legStretch: .0500000007 56 | feetSpacing: 0 57 | rootMotionBoneName: 58 | lastHumanDescriptionAvatarSource: {instanceID: 0} 59 | additionalBone: 1 60 | animationType: 2 61 | userData: 62 | -------------------------------------------------------------------------------- /Assets/Scripts/LSystemParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public static class LSystemParser 5 | { 6 | public static void Parse(string content, out string axiom, out float angle, out int derivations, out Dictionary> productions) 7 | { 8 | axiom = ""; 9 | angle = 0; 10 | derivations = 0; 11 | productions = new Dictionary>(); 12 | var lines = content.Split('\n'); 13 | foreach (string rawLine in lines) 14 | { 15 | string line = rawLine.Trim(); 16 | if (line.Length == 0) 17 | continue; 18 | else if (line.Length == 1 && line[0] == '\r') 19 | continue; 20 | else if (line[0] == '/' && line[1] == '/') 21 | continue; 22 | string value; 23 | if (line.IndexOf("axiom") != -1) 24 | { 25 | value = line.Substring(line.IndexOf("=") + 1); 26 | value = value.Trim(); 27 | axiom = value; 28 | } 29 | else if (line.IndexOf("angle") != -1) 30 | { 31 | value = line.Substring(line.IndexOf("=") + 1); 32 | value = value.Trim(); 33 | angle = float.Parse(value); 34 | } 35 | else if (line.IndexOf("number of derivations") != -1) 36 | { 37 | value = line.Substring(line.IndexOf("=") + 1); 38 | value = value.Trim(); 39 | derivations = int.Parse(value); 40 | } 41 | else 42 | { 43 | string[] tokens = line.Split('='); 44 | if (tokens.Length != 2) 45 | continue; 46 | string predecessor = tokens[0].Trim(); 47 | tokens = tokens[1].Trim().Split(')'); 48 | string probabilityString = tokens[0].Substring(1); 49 | string successor = tokens[1]; 50 | float probability = float.Parse(probabilityString); 51 | if (!productions.ContainsKey(predecessor)) 52 | productions[predecessor] = new List(); 53 | productions[predecessor].Add(new Production(predecessor, successor, probability)); 54 | } 55 | } 56 | if (!ProductionMatcher.CheckProbabilities(productions)) 57 | throw new Exception("There's one of more production rules with probability < 1"); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | L-Systems in Unity 2 | ================ 3 | 4 | L-System is a rewriting system used mainly to model the development of plants. L-Systems in Unity enables you to rapidly create plants in your Unity games. 5 | 6 | 7 | ---------- 8 | 9 | ### Getting Started 10 | 11 | Add LSystemExecutor to a game object in your scene. Set a L-System definition (e.g., "Assets/Defs/3D Tree 1.txt") and rendering properties. Start the game. 12 | 13 | // 3D Tree 1.txt 14 | 15 | axiom=F 16 | angle=22.5 17 | number of derivations=3 18 | 19 | F=(1)F[-&^F][^++&F]||F[--&^F][+&F] 20 | 21 | 22 | ![LSystemExecutor in object inspector](http://pedroboechat.com/images/LSystemInUnity1.png) 23 | 24 | ![3D tree](http://pedroboechat.com/images/LSystemInUnity2.png) 25 | 26 | ---------- 27 | 28 | ### Advanced Usage 29 | 30 | L-System processing is divided into three phases: parsing, derivation and interpretation. L-System on Unity's API map each phase to a static class. 31 | 32 | 33 | ---------- 34 | 35 | #### Parsing 36 | 37 | Method: 38 | 39 | > LSystemParser.Parse() 40 | 41 | Input: 42 | 43 | - l-system definition [string] 44 | 45 | Output: 46 | 47 | - axiom [string] 48 | - angle [float] 49 | - derivations [int] 50 | - productions [Dictionary<string, List<Production>>] 51 | 52 | Example: 53 | 54 | 55 | string axiom; 56 | float angle; 57 | int derivations; 58 | Dictionary> productions; 59 | LSystemParser.Parse( 60 | file.text, 61 | out axiom, 62 | out angle, 63 | out derivations, 64 | out productions); 65 | 66 | 67 | ---------- 68 | 69 | #### Derivation 70 | 71 | Method: 72 | 73 | > LSystemDeriver.Derive() 74 | 75 | Input: 76 | 77 | - axiom [string] 78 | - angle [float] 79 | - derivations [int] 80 | - productions [Dictionary<string, List<Production>>] 81 | 82 | Output: 83 | 84 | - moduleString [string] 85 | 86 | Example: 87 | 88 | string moduleString; 89 | LSystemDeriver.Derive( 90 | axiom, 91 | angle, 92 | derivations, 93 | rules, 94 | out moduleString); 95 | 96 | 97 | ---------- 98 | 99 | #### Interpretation 100 | 101 | Method: 102 | 103 | > LSystemInterpreter.Interpret() 104 | 105 | Input: 106 | 107 | - num. segment axial samplers [int] 108 | - num. segment radial samplers [int] 109 | - segment width [float] 110 | - segment height [float] 111 | - leaf size [float] 112 | - leaf axial density [int] 113 | - leaf radial density [int] 114 | - use foliage [bool] 115 | - narrow branches [bool] 116 | - leaf material [UnityEngine.Material] 117 | - trunk material [UnityEngine.Material] 118 | - angle [float] 119 | - moduleString [string] 120 | 121 | Output: 122 | 123 | - leaves [UnityEngine.GameObject] 124 | - trunk [UnityEngine.GameObject] 125 | 126 | Example: 127 | 128 | 129 | GameObject leaves, trunk; 130 | LSystemInterpreter.Interpret( 131 | segmentAxialSamples, 132 | segmentRadialSamples, 133 | segmentWidth, 134 | segmentHeight, 135 | leafSize, 136 | leafAxialDensity, 137 | leafRadialDensity, 138 | useFoliage, 139 | narrowBranches, 140 | leafMaterial, 141 | trunkMaterial, 142 | angle, 143 | moduleString, 144 | out leaves, 145 | out trunk); 146 | 147 | 148 | -------------------------------------------------------------------------------- /Assets/Scripts/LSystemExecutor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | public class LSystemExecutor : MonoBehaviour 5 | { 6 | [SerializeField] 7 | private TextAsset file; 8 | [SerializeField] 9 | private int segmentAxialSamples = 3; 10 | [SerializeField] 11 | private int segmentRadialSamples = 3; 12 | [SerializeField] 13 | private float segmentWidth; 14 | [SerializeField] 15 | private float segmentHeight; 16 | [SerializeField] 17 | private float leafSize; 18 | [SerializeField] 19 | private int leafAxialDensity = 1; 20 | [SerializeField] 21 | private int leafRadialDensity = 1; 22 | [SerializeField] 23 | private bool useFoliage; 24 | [SerializeField] 25 | private bool narrowBranches = true; 26 | [SerializeField] 27 | private Material trunkMaterial; 28 | [SerializeField] 29 | private Material leafMaterial; 30 | 31 | void Start() 32 | { 33 | string axiom; 34 | float angle; 35 | int derivations; 36 | Dictionary> productions; 37 | LSystemParser.Parse( 38 | file.text, 39 | out axiom, 40 | out angle, 41 | out derivations, 42 | out productions); 43 | 44 | string moduleString; 45 | LSystemDeriver.Derive( 46 | axiom, 47 | angle, 48 | derivations, 49 | productions, 50 | out moduleString); 51 | 52 | GameObject leaves, trunk; 53 | LSystemInterpreter.Interpret( 54 | segmentAxialSamples, 55 | segmentRadialSamples, 56 | segmentWidth, 57 | segmentHeight, 58 | leafSize, 59 | leafAxialDensity, 60 | leafRadialDensity, 61 | useFoliage, 62 | narrowBranches, 63 | leafMaterial, 64 | trunkMaterial, 65 | angle, 66 | moduleString, 67 | out leaves, 68 | out trunk); 69 | 70 | leaves.transform.parent = transform; 71 | leaves.transform.localPosition = Vector3.zero; 72 | trunk.transform.parent = transform; 73 | trunk.transform.localPosition = Vector3.zero; 74 | 75 | UpdateColliderBounds(trunk); 76 | } 77 | 78 | void UpdateColliderBounds(GameObject trunk) 79 | { 80 | // Calculate AABB 81 | Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); 82 | Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); 83 | for (int i = 0; i < trunk.transform.childCount; i++) 84 | { 85 | Transform chunk = trunk.transform.GetChild(i); 86 | min.x = Mathf.Min(min.x, chunk.GetComponent().bounds.min.x); 87 | min.y = Mathf.Min(min.y, chunk.GetComponent().bounds.min.y); 88 | min.z = Mathf.Min(min.z, chunk.GetComponent().bounds.min.z); 89 | max.x = Mathf.Max(max.x, chunk.GetComponent().bounds.max.x); 90 | max.y = Mathf.Max(max.y, chunk.GetComponent().bounds.max.y); 91 | max.z = Mathf.Max(max.z, chunk.GetComponent().bounds.max.z); 92 | } 93 | 94 | Bounds bounds = new Bounds(); 95 | bounds.SetMinMax(min, max); 96 | 97 | BoxCollider collider = gameObject.GetComponent(); 98 | if (collider == null) 99 | return; 100 | collider.center = bounds.center - transform.position; 101 | collider.size = 2 * bounds.extents; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /Assets/Scripts/LSystemInterpreter.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | public static class LSystemInterpreter 6 | { 7 | private struct Turtle 8 | { 9 | public Quaternion direction; 10 | public Vector3 position; 11 | public Vector3 step; 12 | 13 | public Turtle(Turtle other) 14 | { 15 | this.direction = other.direction; 16 | this.position = other.position; 17 | this.step = other.step; 18 | } 19 | 20 | public Turtle(Quaternion direction, Vector3 position, Vector3 step) 21 | { 22 | this.direction = direction; 23 | this.position = position; 24 | this.step = step; 25 | } 26 | 27 | public void Forward() 28 | { 29 | position += direction * step; 30 | } 31 | 32 | public void RotateX(float angle) 33 | { 34 | direction *= Quaternion.Euler(angle, 0, 0); 35 | } 36 | 37 | public void RotateY(float angle) 38 | { 39 | direction *= Quaternion.Euler(0, angle, 0); 40 | } 41 | 42 | public void RotateZ(float angle) 43 | { 44 | direction *= Quaternion.Euler(0, 0, angle); 45 | } 46 | 47 | } 48 | 49 | static void CreateSegment( 50 | int segmentAxisSamples, 51 | int segmentRadialSamples, 52 | float segmentWidth, 53 | float segmentHeight, 54 | bool narrowBranches, 55 | Material trunkMaterial, 56 | Turtle turtle, 57 | int nestingLevel, 58 | ref Mesh currentMesh, 59 | ref int chunkCount, 60 | GameObject trunk, 61 | Dictionary segmentsCache) 62 | { 63 | Vector3[] newVertices; 64 | Vector3[] newNormals; 65 | Vector2[] newUVs; 66 | int[] newIndices; 67 | 68 | Mesh segment; 69 | if (segmentsCache.ContainsKey(nestingLevel)) 70 | segment = segmentsCache[nestingLevel]; 71 | else 72 | { 73 | float thickness = (narrowBranches) ? segmentWidth * (0.5f / (nestingLevel + 1)) : segmentWidth * 0.5f; 74 | segment = ProceduralMeshes.CreateCylinder(segmentAxisSamples, segmentRadialSamples, thickness, segmentHeight); 75 | segmentsCache[nestingLevel] = segment; 76 | } 77 | 78 | newVertices = segment.vertices; 79 | newNormals = segment.normals; 80 | newUVs = segment.uv; 81 | newIndices = segment.triangles; 82 | 83 | if (currentMesh.vertices.Length + newVertices.Length > 65000) 84 | { 85 | CreateNewChunk(currentMesh, ref chunkCount, trunkMaterial, trunk); 86 | currentMesh = new Mesh(); 87 | } 88 | 89 | int numVertices = currentMesh.vertices.Length + newVertices.Length; 90 | int numTriangles = currentMesh.triangles.Length + newIndices.Length; 91 | 92 | Vector3[] vertices = new Vector3[numVertices]; 93 | Vector3[] normals = new Vector3[numVertices]; 94 | int[] indices = new int[numTriangles]; 95 | Vector2[] uvs = new Vector2[numVertices]; 96 | 97 | Array.Copy(currentMesh.vertices, 0, vertices, 0, currentMesh.vertices.Length); 98 | Array.Copy(currentMesh.normals, 0, normals, 0, currentMesh.normals.Length); 99 | Array.Copy(currentMesh.triangles, 0, indices, 0, currentMesh.triangles.Length); 100 | Array.Copy(currentMesh.uv, 0, uvs, 0, currentMesh.uv.Length); 101 | 102 | int offset = currentMesh.vertices.Length; 103 | for (int i = 0; i < newVertices.Length; i++) 104 | vertices[offset + i] = turtle.position + (turtle.direction * newVertices[i]); 105 | 106 | int trianglesOffset = currentMesh.vertices.Length; 107 | offset = currentMesh.triangles.Length; 108 | for (int i = 0; i < newIndices.Length; i++) 109 | indices[offset + i] = (trianglesOffset + newIndices[i]); 110 | 111 | Array.Copy(newNormals, 0, normals, currentMesh.normals.Length, newNormals.Length); 112 | Array.Copy(newUVs, 0, uvs, currentMesh.uv.Length, newUVs.Length); 113 | 114 | currentMesh.vertices = vertices; 115 | currentMesh.normals = normals; 116 | currentMesh.triangles = indices; 117 | currentMesh.uv = uvs; 118 | 119 | currentMesh.Optimize(); 120 | } 121 | 122 | static void AddFoliageAt( 123 | float segmentWidth, 124 | float segmentHeight, 125 | int leafAxialDensity, 126 | int leafRadialDensity, 127 | Turtle turtle, 128 | GameObject leafBillboard, 129 | GameObject leaves) 130 | { 131 | float xAngleStep = -70 / (float)leafAxialDensity, 132 | xAngle = xAngleStep * (leafAxialDensity - 1) - 20, 133 | yAngle = 0, 134 | yAngleStep = 360 / (float)leafRadialDensity, 135 | y = 0, 136 | yStep = -segmentHeight / (float)leafAxialDensity; 137 | for (int i = 0; i < leafAxialDensity; i++, xAngle -= xAngleStep, y -= yStep) 138 | { 139 | for (int j = 0; j < leafRadialDensity; j++, yAngle += yAngleStep) 140 | { 141 | GameObject leaf = (GameObject)GameObject.Instantiate(leafBillboard, Vector3.zero, turtle.direction); 142 | leaf.transform.parent = leaves.transform; 143 | leaf.transform.position = turtle.position - (turtle.direction * new Vector3(0, y, 0)); 144 | leaf.transform.Rotate(new Vector3(xAngle, yAngle, 0)); 145 | } 146 | } 147 | } 148 | 149 | static void CreateNewChunk(Mesh mesh, ref int count, Material trunkMaterial, GameObject trunk) 150 | { 151 | GameObject chunk = new GameObject("Chunk " + (++count)); 152 | chunk.transform.parent = trunk.transform; 153 | chunk.transform.localPosition = Vector3.zero; 154 | chunk.AddComponent().material = trunkMaterial; 155 | chunk.AddComponent().mesh = mesh; 156 | } 157 | 158 | static GameObject CreateLeafBillboard(float leafSize, Material leafMaterial) 159 | { 160 | GameObject leafBillboard = new GameObject("Leaf"); 161 | leafBillboard.AddComponent().sharedMaterial = leafMaterial; 162 | leafBillboard.AddComponent().sharedMesh = ProceduralMeshes.CreateXZPlane(leafSize, leafSize, 1, 1, new Vector3(-leafSize, 0, leafSize * 0.5f)); 163 | return leafBillboard; 164 | } 165 | 166 | public static void Interpret( 167 | int segmentAxisSamples, 168 | int segmentRadialSamples, 169 | float segmentWidth, 170 | float segmentHeight, 171 | float leafSize, 172 | int leafAxialDensity, 173 | int leafRadialDensity, 174 | bool useFoliage, 175 | bool narrowBranches, 176 | Material leafMaterial, 177 | Material trunkMaterial, 178 | float angle, 179 | string moduleString, 180 | out GameObject leaves, 181 | out GameObject trunk) 182 | { 183 | leaves = new GameObject("Leaves"); 184 | trunk = new GameObject("Trunk"); 185 | 186 | GameObject leafBillboard = CreateLeafBillboard(leafSize, leafMaterial); 187 | 188 | int chunkCount = 0; 189 | Mesh currentMesh = new Mesh(); 190 | Dictionary segmentsCache = new Dictionary(); 191 | Turtle current = new Turtle(Quaternion.identity, Vector3.zero, new Vector3(0, segmentHeight, 0)); 192 | Stack stack = new Stack(); 193 | for (int i = 0; i < moduleString.Length; i++) 194 | { 195 | string module = moduleString[i] + ""; 196 | if (module == "F") 197 | { 198 | CreateSegment( 199 | segmentAxisSamples, 200 | segmentRadialSamples, 201 | segmentWidth, 202 | segmentHeight, 203 | narrowBranches, 204 | trunkMaterial, 205 | current, 206 | stack.Count, 207 | ref currentMesh, 208 | ref chunkCount, 209 | trunk, 210 | segmentsCache); 211 | current.Forward(); 212 | } 213 | else if (module == "+") 214 | { 215 | current.RotateZ(angle); 216 | } 217 | else if (module == "-") 218 | { 219 | current.RotateZ(-angle); 220 | } 221 | else if (module == "&") 222 | { 223 | current.RotateX(angle); 224 | } 225 | else if (module == "^") 226 | { 227 | current.RotateX(-angle); 228 | } 229 | else if (module == "\\") 230 | { 231 | current.RotateY(angle); 232 | } 233 | else if (module == "/") 234 | { 235 | current.RotateY(-angle); 236 | } 237 | else if (module == "|") 238 | { 239 | current.RotateZ(180); 240 | } 241 | else if (module == "[") 242 | { 243 | stack.Push(current); 244 | current = new Turtle(current); 245 | } 246 | else if (module == "]") 247 | { 248 | if (useFoliage) 249 | AddFoliageAt( 250 | segmentWidth, 251 | segmentHeight, 252 | leafAxialDensity, 253 | leafRadialDensity, 254 | current, 255 | leafBillboard, 256 | leaves); 257 | current = stack.Pop(); 258 | } 259 | } 260 | CreateNewChunk(currentMesh, ref chunkCount, trunkMaterial, trunk); 261 | GameObject.Destroy(leafBillboard); 262 | } 263 | 264 | } 265 | -------------------------------------------------------------------------------- /Assets/Scripts/ProceduralMeshes.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | 4 | public class ProceduralMeshes 5 | { 6 | 7 | private ProceduralMeshes() 8 | { 9 | } 10 | 11 | public static Mesh CreateXYPlane(float width, float height, int xSegments, int ySegments, Vector3 center) 12 | { 13 | Mesh plane = new Mesh(); 14 | 15 | float xIncrement = width / (float)xSegments; 16 | float yIncrement = height / (float)ySegments; 17 | 18 | int vertexQuantity = (xSegments + 1) * (ySegments + 1); 19 | Vector3[] vertexBuffer = new Vector3[vertexQuantity]; 20 | Vector3[] normalBuffer = new Vector3[vertexQuantity]; 21 | Vector2[] textureCoordinateBuffer = new Vector2[vertexQuantity]; 22 | int i = 0; 23 | Vector3 vScan = center + new Vector3(-(width * 0.5f), -(height * 0.5f), 0); 24 | for (int y = 0; y <= ySegments; y++) 25 | { 26 | Vector3 hScan = vScan; 27 | for (int x = 0; x <= xSegments; x++) 28 | { 29 | hScan += new Vector3(xIncrement, 0, 0); 30 | vertexBuffer[i] = new Vector3(hScan.x, hScan.y, hScan.z); 31 | normalBuffer[i] = Vector3.up; 32 | textureCoordinateBuffer[i] = new Vector2(x / (float)xSegments, y / (float)ySegments); 33 | i++; 34 | } 35 | vScan += new Vector3(0, yIncrement, 0); 36 | } 37 | 38 | i = 0; 39 | int[] indexBuffer = new int[(xSegments * ySegments * 2) * 3]; 40 | for (int y = 1; y <= ySegments; y++) 41 | { 42 | for (int x = 0; x < xSegments; x++) 43 | { 44 | int i0 = (y * (xSegments + 1)) + x; 45 | int i1 = ((y - 1) * (xSegments + 1)) + x; 46 | int i2 = i1 + 1; 47 | int i3 = i0 + 1; 48 | 49 | indexBuffer[i++] = i2; 50 | indexBuffer[i++] = i1; 51 | indexBuffer[i++] = i0; 52 | 53 | indexBuffer[i++] = i2; 54 | indexBuffer[i++] = i0; 55 | indexBuffer[i++] = i3; 56 | } 57 | } 58 | 59 | plane.vertices = vertexBuffer; 60 | plane.triangles = indexBuffer; 61 | plane.normals = normalBuffer; 62 | plane.uv = textureCoordinateBuffer; 63 | 64 | return plane; 65 | } 66 | 67 | public static Mesh CreateXZPlane(float width, float depth, int xSegments, int zSegments, Vector3 center) 68 | { 69 | Mesh plane = new Mesh(); 70 | 71 | float xIncrement = width / (float)xSegments; 72 | float zIncrement = depth / (float)zSegments; 73 | 74 | int vertexQuantity = (xSegments + 1) * (zSegments + 1); 75 | Vector3[] vertexBuffer = new Vector3[vertexQuantity]; 76 | Vector3[] normalBuffer = new Vector3[vertexQuantity]; 77 | Vector2[] textureCoordinateBuffer = new Vector2[vertexQuantity]; 78 | int i = 0; 79 | Vector3 vScan = center + new Vector3(-(width * 0.5f), 0, -(depth * 0.5f)); 80 | for (int z = 0; z <= zSegments; z++) 81 | { 82 | Vector3 hScan = vScan; 83 | for (int x = 0; x <= xSegments; x++) 84 | { 85 | hScan += new Vector3(xIncrement, 0, 0); 86 | vertexBuffer[i] = new Vector3(hScan.x, hScan.y, hScan.z); 87 | normalBuffer[i] = Vector3.up; 88 | textureCoordinateBuffer[i] = new Vector2(x / (float)xSegments, z / (float)zSegments); 89 | i++; 90 | } 91 | vScan += new Vector3(0, 0, zIncrement); 92 | } 93 | 94 | i = 0; 95 | int[] indexBuffer = new int[(xSegments * zSegments * 2) * 3]; 96 | for (int z = 1; z <= zSegments; z++) 97 | { 98 | for (int x = 0; x < xSegments; x++) 99 | { 100 | int i0 = (z * (xSegments + 1)) + x; 101 | int i1 = ((z - 1) * (xSegments + 1)) + x; 102 | int i2 = i1 + 1; 103 | int i3 = i0 + 1; 104 | 105 | indexBuffer[i++] = i2; 106 | indexBuffer[i++] = i1; 107 | indexBuffer[i++] = i0; 108 | 109 | indexBuffer[i++] = i2; 110 | indexBuffer[i++] = i0; 111 | indexBuffer[i++] = i3; 112 | } 113 | } 114 | 115 | plane.vertices = vertexBuffer; 116 | plane.triangles = indexBuffer; 117 | plane.normals = normalBuffer; 118 | plane.uv = textureCoordinateBuffer; 119 | 120 | return plane; 121 | } 122 | 123 | public static Mesh CreateCylinder(int axisSamples, int radialSamples, float radius, float height) 124 | { 125 | Mesh mesh = new Mesh(); 126 | 127 | int numVertices = axisSamples * (radialSamples + 1); 128 | int numIndices = 3 * (2 * (axisSamples - 1) * radialSamples); 129 | 130 | // Create a vertex buffer. 131 | Vector3[] vertices = new Vector3[numVertices]; 132 | Vector3[] normals = new Vector3[numVertices]; 133 | Vector2[] uvs = new Vector2[numVertices]; 134 | int[] indices = new int[numIndices]; 135 | 136 | // Generate geometry. 137 | float invRS = 1.0f / (float)radialSamples; 138 | float invASm1 = 1.0f / (float)(axisSamples - 1); 139 | int r, a, aStart, i; 140 | 141 | // Generate points on the unit circle to be used in computing the mesh points on a cylinder slice. 142 | float[] cs = new float[radialSamples + 1]; 143 | float[] sn = new float[radialSamples + 1]; 144 | for (r = 0; r < radialSamples; ++r) 145 | { 146 | float angle = Mathf.PI * 2 * invRS * r; 147 | cs[r] = Mathf.Cos(angle); 148 | sn[r] = Mathf.Sin(angle); 149 | } 150 | cs[radialSamples] = cs[0]; 151 | sn[radialSamples] = sn[0]; 152 | 153 | Vector2 uv; 154 | 155 | // Generate the cylinder itself. 156 | for (a = 0, i = 0; a < axisSamples; ++a) 157 | { 158 | float axisFraction = a * invASm1; // in [0,1] 159 | float y = height * axisFraction; 160 | 161 | // Compute center of slice. 162 | Vector3 sliceCenter = new Vector3(0, y, 0); 163 | 164 | // Compute slice vertices with duplication at endpoint. 165 | int save = i; 166 | for (r = 0; r < radialSamples; ++r) 167 | { 168 | float radialFraction = r * invRS; // in [0,1) 169 | Vector3 normal = new Vector3(sn[r], 0, cs[r]); 170 | 171 | vertices[i] = sliceCenter + radius * normal; 172 | normals[i] = normal.normalized; 173 | 174 | uv = new Vector2(radialFraction, axisFraction); 175 | uvs[i] = uv; 176 | 177 | ++i; 178 | } 179 | 180 | vertices[i] = vertices[save]; 181 | normals[i] = normals[save]; 182 | 183 | uv = new Vector2(1.0f, axisFraction); 184 | uvs[i] = uv; 185 | 186 | ++i; 187 | } 188 | 189 | // Generate indices. 190 | int c = 0; 191 | for (a = 0, aStart = 0; a < axisSamples - 1; ++a) 192 | { 193 | int i0 = aStart; 194 | int i1 = i0 + 1; 195 | aStart += radialSamples + 1; 196 | int i2 = aStart; 197 | int i3 = i2 + 1; 198 | for (i = 0; i < radialSamples; ++i) 199 | { 200 | indices[c++] = i0++; 201 | indices[c++] = i1; 202 | indices[c++] = i2; 203 | indices[c++] = i1++; 204 | indices[c++] = i3++; 205 | indices[c++] = i2++; 206 | } 207 | } 208 | 209 | mesh.vertices = vertices; 210 | mesh.triangles = indices; 211 | mesh.normals = normals; 212 | mesh.uv = uvs; 213 | mesh.RecalculateBounds(); 214 | 215 | return mesh; 216 | } 217 | 218 | public static Mesh CreateSphere(float radius, int zSegments, int radialSegments) 219 | { 220 | Mesh sphere = new Mesh(); 221 | 222 | int vertexQuantity = (zSegments - 2) * (radialSegments + 1) + 2; 223 | int triangleQuantity = 2 * (zSegments - 2) * radialSegments; 224 | 225 | Vector3[] vertexBuffer = new Vector3[vertexQuantity]; 226 | Vector2[] textureCoordinateBuffer = new Vector2[vertexQuantity]; 227 | Color[] colorBuffer = new Color[vertexQuantity]; 228 | int[] indexBuffer = new int[3 * triangleQuantity]; 229 | 230 | // generate geometry 231 | float radialFactor = 1.0F / (radialSegments); 232 | float zFactor = 2.0F / (zSegments - 1); 233 | 234 | // Generate points on the unit circle to be used in computing the mesh 235 | // points on a cylinder slice. 236 | float[] sines = new float[radialSegments + 1]; 237 | float[] cossines = new float[radialSegments + 1]; 238 | for (int radialIndex = 0; radialIndex < radialSegments; radialIndex++) 239 | { 240 | float angle = (2 * Mathf.PI) * radialFactor * radialIndex; 241 | cossines[radialIndex] = Mathf.Cos(angle); 242 | sines[radialIndex] = Mathf.Sin(angle); 243 | } 244 | 245 | sines[radialSegments] = sines[0]; 246 | cossines[radialSegments] = cossines[0]; 247 | 248 | // generate the sphere itself 249 | int i = 0; 250 | Vector3 vertex; 251 | for (int zIndex = 1; zIndex < zSegments - 1; zIndex++) 252 | { 253 | float zFraction = -1.0F + zFactor * zIndex; // in (-1,1) 254 | float z = radius * zFraction; 255 | 256 | // compute center of slice 257 | Vector3 sliceCenter = new Vector3(0.0F, 0.0F, z); 258 | 259 | // compute radius of slice 260 | float sliceRadius = Mathf.Sqrt(Mathf.Abs(radius * radius - z * z)); 261 | 262 | // compute slice vertices with duplication at end point 263 | int savedI = i; 264 | for (int radialIndex = 0; radialIndex < radialSegments; radialIndex++) 265 | { 266 | float radialFraction = radialIndex * radialFactor; // in [0,1) 267 | Vector3 radial = new Vector3(cossines[radialIndex], sines[radialIndex], 0.0F); 268 | vertexBuffer[i] = sliceCenter + sliceRadius * radial; 269 | 270 | vertex = vertexBuffer[i]; 271 | vertex.Normalize(); 272 | colorBuffer[i] = new Color(vertex.x, vertex.y, vertex.z); 273 | 274 | textureCoordinateBuffer[i] = new Vector2(radialFraction, 0.5F * (zFraction + 1.0F)); 275 | 276 | i++; 277 | } 278 | 279 | vertexBuffer[i] = vertexBuffer[savedI]; 280 | 281 | vertex = vertexBuffer[i]; 282 | vertex.Normalize(); 283 | colorBuffer[i] = new Color(vertex.x, vertex.y, vertex.z); 284 | 285 | textureCoordinateBuffer[i] = new Vector2(1.0F, 0.5F * (zFraction + 1.0F)); 286 | 287 | i++; 288 | } 289 | 290 | // south pole 291 | vertexBuffer[i] = -radius * Vector3.forward; 292 | 293 | vertex = vertexBuffer[i]; 294 | vertex.Normalize(); 295 | colorBuffer[i] = new Color(vertex.x, vertex.y, vertex.z); 296 | 297 | textureCoordinateBuffer[i] = new Vector2(0.5F, 0.5F); 298 | 299 | i++; 300 | 301 | // north pole 302 | vertexBuffer[i] = radius * Vector3.forward; 303 | 304 | vertex = vertexBuffer[i]; 305 | vertex.Normalize(); 306 | colorBuffer[i] = new Color(vertex.x, vertex.y, vertex.z); 307 | 308 | textureCoordinateBuffer[i] = new Vector2(0.5F, 1.0F); 309 | 310 | i++; 311 | 312 | if (i != vertexQuantity) 313 | { 314 | // TODO: 315 | throw new Exception(""); 316 | } 317 | 318 | if (!(radialSegments < (32768 - 1) && (radialSegments >= 0))) 319 | { 320 | // TODO: 321 | throw new Exception(""); 322 | } 323 | 324 | int radialSegmentsCount = radialSegments; 325 | if (!(vertexQuantity < (32768) && (vertexQuantity >= 0))) 326 | { 327 | // TODO: 328 | throw new Exception(""); 329 | } 330 | 331 | int vq = vertexQuantity; 332 | if (!(zSegments < (32768) && (zSegments >= 0))) 333 | { 334 | // TODO: 335 | throw new Exception(""); 336 | } 337 | 338 | int zSegmentsCount = zSegments; 339 | 340 | // generate connectivity 341 | int iZStart = 0; 342 | int connectivityIndex = 0; 343 | for (int iZ = 0; iZ < zSegments - 3; iZ++) 344 | { 345 | int i0 = iZStart; 346 | int i1 = i0 + 1; 347 | iZStart += radialSegmentsCount + 1; 348 | int i2 = iZStart; 349 | int i3 = i2 + 1; 350 | for (int j = 0; j < radialSegments; j++) 351 | { 352 | indexBuffer[connectivityIndex] = i0++; 353 | indexBuffer[connectivityIndex + 1] = i2; 354 | indexBuffer[connectivityIndex + 2] = i1; 355 | indexBuffer[connectivityIndex + 3] = i1++; 356 | indexBuffer[connectivityIndex + 4] = i2++; 357 | indexBuffer[connectivityIndex + 5] = i3++; 358 | connectivityIndex += 6; 359 | } 360 | } 361 | 362 | // south pole triangles 363 | int vQm2 = vq - 2; 364 | //c = 0; 365 | for (int j = 0; j < radialSegmentsCount; j++) 366 | { 367 | indexBuffer[connectivityIndex] = j; 368 | indexBuffer[connectivityIndex + 1] = j + 1; 369 | indexBuffer[connectivityIndex + 2] = vQm2; 370 | connectivityIndex += 3; 371 | } 372 | 373 | // north pole triangles 374 | int vQm1 = vq - 1; 375 | int offset = (zSegmentsCount - 3) * (radialSegmentsCount + 1); 376 | //c = 0; 377 | for (int j = 0; j < radialSegmentsCount; j++) 378 | { 379 | indexBuffer[connectivityIndex] = j + offset; 380 | indexBuffer[connectivityIndex + 1] = vQm1; 381 | indexBuffer[connectivityIndex + 2] = j + 1 + offset; 382 | connectivityIndex += 3; 383 | } 384 | 385 | if (indexBuffer.Length != 3 * triangleQuantity) 386 | { 387 | // TODO: 388 | throw new Exception(""); 389 | } 390 | 391 | sphere.vertices = vertexBuffer; 392 | sphere.colors = colorBuffer; 393 | sphere.uv = textureCoordinateBuffer; 394 | 395 | // invert triangles to right-hand 396 | for (int j = 0; j < indexBuffer.Length; j += 3) 397 | { 398 | int a = indexBuffer[j]; 399 | int c = indexBuffer[j + 2]; 400 | indexBuffer[j] = c; 401 | indexBuffer[j + 2] = a; 402 | } 403 | 404 | sphere.triangles = indexBuffer; 405 | 406 | sphere.RecalculateNormals(); 407 | // sphere.Optimize(); 408 | 409 | return sphere; 410 | } 411 | } 412 | --------------------------------------------------------------------------------