public override void Execute(ParameterBundle bundle) { if (dead) { return; } AnyExecute(bundle); }
public ParameterBundle(ParameterBundle original) { //Make new dictionary and add values parameters = new Dictionary <string, object>(original.parameters.Count, original.parameters.Comparer); foreach (KeyValuePair <string, object> pair in original.parameters) { parameters.Add(pair.Key, pair.Value); } }
// Encapsulates common functionality between Bake and Execute. protected void AnyExecute(ParameterBundle bundle) { Sentence sentence; CharGameObjectDict implementations; RuleSet rules; if (!GetCoreParameters(bundle, out sentence, out implementations, out rules)) { return; } // Count brackets to determine which sentence piece to pull into a new fork int bracketCount = 1; sentence.PushPosition(); while (sentence.HasNext()) { char c = sentence.Next(); if (c == splitEndDelimiter) { bracketCount--; } else if (c == symbol) { bracketCount++; } if (bracketCount == 0) { break; } } Sentence split = sentence.PopPositionAndCut(); //call next recursion if (baked) { BakeNextModule(transform, sentence, implementations, rules, bundle); BakeNextModule(transform, split, implementations, rules, bundle); } else { EnqueueProcessNextModule(transform, sentence, implementations, rules, bundle); EnqueueProcessNextModule(transform, split, implementations, rules, bundle); } Destroy(gameObject); }
public bool GetPositionParameters(ParameterBundle bundle, out int generation, out Quaternion rotation) { bool success = true; if (!bundle.Get("Generation", out generation)) { success = false; Debug.LogError("Default parameter 'Generation' missing.", gameObject); } if (!bundle.Get("Rotation", out rotation)) { success = false; Debug.LogError("Default parameter 'Rotation' missing.", gameObject); } return(success); }
/// <summary> /// Merge multiple ParameterBundles into one. Note that parameters with the same key will override each other and parameters with the same key but different value will be ignored /// </summary> /// <param name="bundles"></param> /// <returns></returns> public static ParameterBundle Merge(List <ParameterBundle> bundles) { ParameterBundle merged = new ParameterBundle(); foreach (ParameterBundle bundle in bundles) { foreach (KeyValuePair <string, object> pair in bundle.parameters) { if (!merged.Set(pair.Key, pair.Value)) { merged.Put(pair.Key, pair.Value); } } } return(merged); }
// Entry point when pre-baking LSystem. public override void Bake(ParameterBundle bundle) { if ((filter = gameObject.GetComponent <MeshFilter>()) == null) { filter = gameObject.AddComponent <MeshFilter>(); } if ((leafRenderer = gameObject.GetComponent <MeshRenderer>()) == null) { leafRenderer = gameObject.AddComponent <MeshRenderer>(); } leafRenderer.material = leafMaterial; //Get parameters Quaternion rotation; Sentence sentence; CharGameObjectDict implementations; int generation; RuleSet rules; // Check valid state for growing if (!GetCoreParameters(bundle, out sentence, out implementations, out rules) || !GetPositionParameters(bundle, out generation, out rotation)) { return; } transform.position = previous.transform.position; transform.rotation = rotation; MeshFilter sharedFilter; if (bakedPrefabFilters.TryGetValue(prefabIdentifier, out sharedFilter)) { filter.mesh = sharedFilter.sharedMesh; } else { UpdateMesh(1, offset); bakedPrefabFilters.Add(prefabIdentifier, filter); } if (setStaticOnComplete) { gameObject.isStatic = true; } BakeNextModule(transform, sentence, implementations, rules, bundle); }
// Once ProcessNextModule has found an executable module public void ExecuteModule(Module previous, KeyValuePair <GameObject, Sentence> moduleSentancePair, ParameterBundle bundle, char symbol, bool baked) { if (previous == null) { return; // if object is destroyed externally } Profiler.BeginSample("LSystem.Module.ExecuteModule"); if (moduleSentancePair.Key == null) { return; } ParameterBundle newBundle = new ParameterBundle(bundle); newBundle.SetOrPut("Sentence", moduleSentancePair.Value); GameObject moduleInstance = UnityEngine.Object.Instantiate(moduleSentancePair.Key); Module module = moduleInstance.GetComponent <Module>(); module.previous = previous; moduleInstance.transform.SetParent(previous.transform, true); module.symbol = symbol; //Seeds are baked separately so their baked value must not be overwritten. //Is there a way to avoid this special case in module? if (module.GetType() != typeof(Seed)) { module.baked = baked; } Profiler.EndSample(); if (prefabIdentifier != null) { module.prefabIdentifier = prefabIdentifier; } if (baked) { module.Bake(newBundle); } else { module.Execute(newBundle); } }
// Entry point when dynamically executing LSystem. public override void Execute(ParameterBundle bundle) { if ((filter = gameObject.GetComponent <MeshFilter>()) == null) { filter = gameObject.AddComponent <MeshFilter>(); } if ((leafRenderer = gameObject.GetComponent <MeshRenderer>()) == null) { leafRenderer = gameObject.AddComponent <MeshRenderer>(); } leafRenderer.material = leafMaterial; if (gameObject.activeSelf) { StartCoroutine(Grow(bundle)); } }
// Encapsulates common functionality between Bake and Execute. protected void AnyExecute(ParameterBundle bundle) { //try get relevant prams. bool fatal = false; Sentence sentence; CharGameObjectDict implementations; RuleSet rules; if (!GetCoreParameters(bundle, out sentence, out implementations, out rules)) { fatal = true; } // Perform rotation on transform based. // Note that many rotations are not easily possible with this method. Needs to be improved. Quaternion rotation; if (bundle.Get("Rotation", out rotation)) { //apply pitch yaw and roll to the rotation rotation = rotation * Quaternion.Euler(new Vector3(Random.Range(eulerAnglesMin.x, eulerAnglesMax.x), Random.Range(eulerAnglesMin.y, eulerAnglesMax.y), Random.Range(eulerAnglesMin.z, eulerAnglesMax.z))); bundle.Set("Rotation", rotation); } else { Debug.LogWarning("Default parameter 'Rotation' missing. Skipping Rotation", gameObject); } if (!fatal) { //Call next module if (baked) { BakeNextModule(transform, sentence, implementations, rules, bundle); } else { EnqueueProcessNextModule(transform, sentence, implementations, rules, bundle); } } Destroy(gameObject); //once rotation and next module clled we can destroy the object. }
// Retrial for parameters common to most LSystems are wrapped here for convenience. public bool GetCoreParameters(ParameterBundle bundle, out Sentence sentence, out CharGameObjectDict implementations, out RuleSet rules) { bool success = true; if (!bundle.Get("Sentence", out sentence)) { success = false; Debug.LogError("Default parameter 'Sentence' missing.", gameObject); } if (!bundle.Get("Implementations", out implementations)) { success = false; Debug.LogError("Default parameter 'Implementations' missing.", gameObject); } if (!bundle.Get("RuleSet", out rules)) { success = false; Debug.LogError("Default parameter 'RuleSet' missing.", gameObject); } return(success); }
// Execute next module as a bake (no delay) operation. public void BakeNextModule(Transform caller, Sentence sentence, SerializableDictionary <char, GameObject> implementation, RuleSet rules, ParameterBundle bundle) { Module previous; if (this.ethereal) { // Since this module is ethereal we have to set the parent to this modules parent // Note that this behavior is recursive up to the first non ethereal module or null. previous = caller.parent.GetComponent <Module>(); } else { previous = caller.GetComponent <Module>(); } ProcessNextModule(previous, sentence, implementation, rules, bundle, true); }
public override void Bake(ParameterBundle bundle) { if (dead) { return; } Quaternion rotation; if (!bundle.Get("Rotation", out rotation)) { //TODO: used to be Vector3.up. Equivelent? rotation = Quaternion.identity; } Vector3 position = transform.position; Transform parent = null; if (previous != null) { position = previous.transform.position; parent = previous.transform; } Quaternion bakedRot = Quaternion.Euler(UnityEngine.Random.Range(bakedRotationMin.x, bakedRotationMax.x), UnityEngine.Random.Range(bakedRotationMin.y, bakedRotationMax.y), UnityEngine.Random.Range(bakedRotationMin.z, bakedRotationMax.z)); float s = UnityEngine.Random.Range(bakedScaleMin, bakedScaleMax); Vector3 scale = new Vector3(s, s, s); // try get existing instance GameObject prototypeInstance; GameObject instance; if (bakedProtoypes.TryGetValue(prefabIdentifier, out prototypeInstance)) { if (parent == null) { parent = transform; } instance = (GameObject)Instantiate(prototypeInstance, position, bakedRot, parent); instance.transform.localScale = scale; instance.name = "Instance_" + prefabIdentifier; instance.transform.rotation = rotation; instance.SetActive(true); if (bakedScaleOnSpawn) { StartCoroutine(BakedScale(instance)); } } else { //TODO: This could be done off-line. AnyExecute(bundle); //bake transform.position = Vector3.zero; transform.rotation = Quaternion.identity; transform.parent = null; // The current object contains the original mesh data so it needs to become the prototypical instance. we create a copy of it to continue. instance = (GameObject)Instantiate(gameObject, position, rotation, parent); instance.transform.localScale = scale; instance.name = "InitialInstance_" + prefabIdentifier; if (bakedScaleOnSpawn) { StartCoroutine(BakedScale(instance)); } //Kill initial instance module since its already generated. Module initialInstanceModule = instance.GetComponent <Module>(); if (initialInstanceModule != null) { Kill(initialInstanceModule); } gameObject.name = "Prototype_" + prefabIdentifier; Kill(this); //the prototype must not be able to create new instances(they will do the same... etc) gameObject.hideFlags = HideFlags.HideInHierarchy; bakedProtoypes.Add(prefabIdentifier, gameObject); gameObject.SetActive(false); } }
/// <summary> /// A unified LSystem symbol implementation execution point with a parameter list for pre-baked execution. /// </summary> /// <param name="bundle">Bundle containing information collected by previous iterations</param> /// <returns>Bundle containing any information that previous Modules may be interested in.</returns>> public abstract void Bake(ParameterBundle bundle);
public void AnyExecute(ParameterBundle bundle) { Profiler.BeginSample("LSystem.Seed.Execute"); if (previous != null) { transform.position = previous.transform.position; } Sentence sentence = new Sentence(axiom); if (generateMode == GenMode.PreEdgeRewrite) { //Pre calculate final sentence. for (int i = 0; i < iterations; i++) { sentence = rules.NextGeneration(sentence); } rules.Fertile = false; bundle.SetOrPut("Iterations", 0); } else // if(preGrow == GenMode.IterateNodeRewrite) { rules.Fertile = true; bundle.SetOrPut("Iterations", iterations); } bundle.SetOrPut("Generation", 0); bundle.SetOrPut("Sentence", sentence); bundle.SetOrPut("Implementations", implementations); bundle.SetOrPut("RuleSet", rules); if (!bundle.Exists("Position")) { bundle.Put("Position", transform.position); } if (!inheritRotation) { bundle.SetOrPut("Rotation", transform.rotation); } else if (!bundle.Exists("Rotation")) { bundle.Put("Rotation", transform.rotation); } // execute modules in the axiom if (!executed) { executed = true; if (baked) { BakeNextModule(transform, sentence, implementations, rules, bundle); } else { EnqueueProcessNextModule(transform, sentence, implementations, rules, bundle); } } Profiler.EndSample(); }
// Run the update loop to grow the mesh when not baked IEnumerator Grow(ParameterBundle bundle) { //Get parameters Quaternion rotation; Sentence sentence; CharGameObjectDict implementations; int generation; RuleSet rules; // Check valid state for growing if (!GetCoreParameters(bundle, out sentence, out implementations, out rules) || !GetPositionParameters(bundle, out generation, out rotation)) { yield break; } float medialSize, lateralSize, growTime; if (bundle.Get("LeafMedialSize", out medialSize)) { startMedialSize = medialSize; } if (bundle.Get("LeafLateralSize", out lateralSize)) { startLateralSize = lateralSize; } if (bundle.Get("LeafGrowTime", out growTime)) { startGrowTime = growTime; } transform.position = previous.transform.position; transform.rotation = rotation; float time = 1f; if (animate) { time = 0f; } while (time < startGrowTime) { time += Time.deltaTime; UpdateMesh(time / startGrowTime, offset); yield return(null); } bundle.SetOrPut("LeafMedialSize", startMedialSize * medialSizeChangeCoefficient); bundle.SetOrPut("LeafLateralSize", startLateralSize * lateralSizeChangeCoefficient); bundle.SetOrPut("LeafGrowTime", startGrowTime * growTimeChangeCoefficient); if (setStaticOnComplete) { gameObject.isStatic = true; } EnqueueProcessNextModule(transform, sentence, implementations, rules, bundle); if (continuousUpdate) { while (true) { UpdateMesh(1f, offset); yield return(null); } } }
IEnumerator Grow(ParameterBundle bundle) { //Get parameters Quaternion rotation; Sentence sentence; CharGameObjectDict implementations; int generation; RuleSet rules; // Check valid state for growing if (!GetCoreParameters(bundle, out sentence, out implementations, out rules) || !GetPositionParameters(bundle, out generation, out rotation)) { yield break; } // Setup Renderer if ((filter = gameObject.GetComponent <MeshFilter>()) == null) { filter = gameObject.AddComponent <MeshFilter>(); } if ((branchRenderer = gameObject.GetComponent <MeshRenderer>()) == null) { branchRenderer = gameObject.AddComponent <MeshRenderer>(); } branchRenderer.material = branchMaterial; // Match start position to previous position. As growth progresses position will be offset // While rotation stays the same transform.position = previous.transform.position; transform.rotation = rotation; // Try and pick up length and radius where last branch left off. float length, radius, growSpeed; if (bundle.Get("BranchLength", out length)) { startLength = length; } if (bundle.Get("BranchRadius", out radius)) { startRadius = radius; } if (bundle.Get("BranchGrowSpeed", out growSpeed)) { startGrowSpeed = growSpeed; } if (bundle.Get("BranchFaceNum", out faces)) { startFaceNum = faces; } GrowLoopCallback growLoopCallback; bundle.Get("GrowLoopCallback", out growLoopCallback); radius = startRadius; length = startLength; growSpeed = startGrowSpeed; faces = startFaceNum; // Since we don't want to continue drawing where it doesn't matter! // Note that future emergence is killed here. if (length < lengthCutoff || radius < radiusCutoff) { yield break; } if (endObject != null) { endObject = (GameObject)Instantiate(endObject, transform); Module mod = endObject.GetComponent <Module>(); if (mod != null) { AssignPrevious(mod, this); if (mod.GetType() != typeof(Seed)) { Kill(mod); mod.Execute(bundle); } } endObject.transform.rotation = rotation; } // Update mesh and extend transform towards final position float distance = Vector3.Distance(transform.position, previous.transform.position); while (distance < length) { float completionRatio = distance / length; transform.position += transform.up * Mathf.Min(/*heading.magnitude * */ Time.deltaTime * Mathf.Lerp(startGrowSpeed, growSpeed * growSpeedChangeCoefficient, completionRatio), length); distance = Vector3.Distance(transform.position, previous.transform.position); bottomRadius = Mathf.Lerp(0, startRadius, completionRatio); UpdateBranch(Vector3.up * -distance, distance, bottomRadius * bottomRadiusMultiplier, topRadius * topRadiusMultiplier, Mathf.Max(2, (int)(faces))); if (growLoopCallback != null) { growLoopCallback(bottomRadius); } if (endObject != null) { endObject.transform.position = transform.position; endObject.transform.rotation = rotation; } yield return(null); } bottomRadius = startRadius; // Update parameters for next branch bundle.SetOrPut("BranchLength", length * lengthChangeCoefficient); bundle.SetOrPut("BranchRadius", radius * radiusChangeCoefficient); bundle.SetOrPut("BranchGrowSpeed", growSpeed * growSpeedChangeCoefficient); bundle.SetOrPut("BranchFaceNum", faces * faceNumChangeCoefficient); // For coordination between branches growLoopCallback = NextBranchGrowLoopCallback; bundle.SetOrPut("GrowLoopCallback", growLoopCallback); if (setStaticOnComplete) { gameObject.isStatic = true; } EnqueueProcessNextModule(transform, sentence, implementations, rules, bundle); }
// Execute next module as a IEnumerator (undefined delay based on load) operation. public void EnqueueProcessNextModule(Transform caller, Sentence sentence, SerializableDictionary <char, GameObject> implementation, RuleSet rules, ParameterBundle bundle) { if (sheduler == null) { sheduler = FindObjectOfType <ModuleSheduler>(); if (sheduler == null) { GameObject shedulerObject = new GameObject("LSystemModuleSheduler"); sheduler = shedulerObject.AddComponent <ModuleSheduler>(); shedulerObject.hideFlags = HideFlags.HideInHierarchy; } } Module previous; if (this.ethereal) { // Since this module is ethereal we have to set the parent to this modules parent // Note that this behavior is recursive up to the first non ethereal module or null. previous = caller.parent.GetComponent <Module>(); } else { previous = caller.GetComponent <Module>(); } sheduler.EnqueueProcessNextModule(EnumerableProcessNextModule(previous, sentence, implementation, rules, bundle)); }
// Entry point when dynamically generating LSystem. public override void Execute(ParameterBundle bundle) { AnyExecute(bundle); }
// Called from the module scheduler when there is time. IEnumerator EnumerableProcessNextModule(Module previous, Sentence sentence, SerializableDictionary <char, GameObject> implementation, RuleSet rules, ParameterBundle bundle) { if (!ProcessNextModule(previous, sentence, implementation, rules, bundle, false)) { yield break; } }
// Entry point when dynamically executing LSystem. public override void Execute(ParameterBundle bundle) { StartCoroutine(Grow(bundle)); }
// Parse the sentence, load symbol implementations and find the next module to execute. // One module is executed at a time, and an internal pointer is adjusted to for the next iteration. public bool ProcessNextModule(Module previous, Sentence sentence, SerializableDictionary <char, GameObject> implementation, RuleSet rules, ParameterBundle bundle, bool baked) { if (dead) { return(false); } Profiler.BeginSample("LSystem.Module.ProcessNextModule"); GameObject module; char symbol = '\0'; do { if (!sentence.HasNext()) { sentence = rules.NextGeneration(sentence); if (!bundle.Set("Sentence", sentence)) { Debug.LogError("Cannot set 'Sentence' parameter in GetAndExecuteModule", gameObject); } int generation; int iterations; if (bundle.Get("Generation", out generation)) { generation++; if (bundle.Get("Iterations", out iterations)) { if (generation > iterations) { //Max iterations reached. return(false); } } if (!bundle.Set("Generation", generation)) { Debug.LogError("Cannot set 'Generation' parameter in GetAndExecuteModule", gameObject); } } else { Debug.LogError("Cannot get 'Generation' parameter in GetAndExecuteModule", gameObject); } } symbol = sentence.Next(); if (symbol == '\0') { return(false); //Sentence is empty! Caused if rules do not generate anything from previous } } while (!implementation.TryGetValue(symbol, out module)); KeyValuePair <GameObject, Sentence> newPair = new KeyValuePair <GameObject, Sentence>(module, sentence); Profiler.EndSample(); ExecuteModule(previous, newPair, bundle, symbol, baked); return(true); }
// Entry point when pre-baking LSystem. public override void Bake(ParameterBundle bundle) { //Get parameters Quaternion rotation; Sentence sentence; CharGameObjectDict implementations; int generation; RuleSet rules; // Check valid state for growing if (!GetCoreParameters(bundle, out sentence, out implementations, out rules) || !GetPositionParameters(bundle, out generation, out rotation)) { return; } // Setup Renderer if ((filter = gameObject.GetComponent <MeshFilter>()) == null) { filter = gameObject.AddComponent <MeshFilter>(); } if ((branchRenderer = gameObject.GetComponent <MeshRenderer>()) == null) { branchRenderer = gameObject.AddComponent <MeshRenderer>(); } branchRenderer.material = branchMaterial; //Match start position to previous position. As growth progresses position will be offset //While rotation stays the same transform.position = previous.transform.position; transform.rotation = rotation; // Try and pick up length and radius where last branch left off. float length, radius; if (bundle.Get("BranchLength", out length)) { startLength = length; } if (bundle.Get("BranchRadius", out radius)) { startRadius = radius; } if (bundle.Get("BranchFaceNum", out faces)) { startFaceNum = faces; } int branchIndex = 0; bundle.Get("BakedBranchIndex", out branchIndex); GrowLoopCallback growLoopCallback; bundle.Get("GrowLoopCallback", out growLoopCallback); radius = startRadius; length = startLength; faces = startFaceNum; // Since we don't want to continue drawing where it doesn't matter! // Note that future emergence is killed here. if (length < lengthCutoff || radius < radiusCutoff) { return; } // Update end object to final position and execute if (endObject != null) { endObject = (GameObject)Instantiate(endObject, transform); Module mod = endObject.GetComponent <Module>(); if (mod != null) { AssignPrevious(mod, this); if (mod.GetType() != typeof(Seed)) { Kill(mod); SetPrefabIdentifier(mod); mod.Bake(bundle); } } endObject.transform.rotation = rotation; } //TODO: used to be the heading.. does this work? transform.position += transform.up * length; // Bake or reuse mesh. // Meshes are reused based on prefabIdentifier bool meshNeeded = false; List <MeshFilter> sharedFilters; if (bakedPrefabFilters.TryGetValue(prefabIdentifier, out sharedFilters)) { if (sharedFilters.Count > branchIndex) { filter.mesh = sharedFilters[branchIndex].sharedMesh; } else { meshNeeded = true; } } else { bakedPrefabFilters.Add(prefabIdentifier, new List <MeshFilter>()); meshNeeded = true; } if (meshNeeded) { float distance = Vector3.Distance(transform.position, previous.transform.position); bottomRadius = startRadius; UpdateBranch(Vector3.up * -distance, distance, bottomRadius * bottomRadiusMultiplier, radius * radiusChangeCoefficient, Mathf.Max(2, (int)(faces))); } if (endObject != null) { endObject.transform.position = transform.position; endObject.transform.rotation = rotation; } // Update parameters for next branch bundle.SetOrPut("BranchLength", length * lengthChangeCoefficient); bundle.SetOrPut("BranchRadius", radius * radiusChangeCoefficient); bundle.SetOrPut("BranchFaceNum", faces * faceNumChangeCoefficient); bundle.SetOrPut("BakedBranchIndex", ++branchIndex); if (setStaticOnComplete) { gameObject.isStatic = true; } BakeNextModule(transform, sentence, implementations, rules, bundle); }
/// <summary> /// A unified LSystem symbol implementation execution point with a parameter list for dynamic execution. /// </summary> /// <param name="bundle">Bundle containing information collected by previous iterations</param> /// <returns>Bundle containing any information that previous Modules may be interested in.</returns> public abstract void Execute(ParameterBundle bundle);