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