// 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); }
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); }
// 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); }
// 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. }
// 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); }
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); }
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); } }
// 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); } } }