public TrunkMetamerConstraint(Metamer metamer, Vector2 anchorPoint, float distance) : base(ConstraintType.TRUNK) { this.metamer = metamer; this.anchorPoint = anchorPoint; this.distance = distance; }
public DistanceMetamerConstraint(Metamer metamerA, Metamer metamerB, float distance, float stiffness) : base(ConstraintType.DISTANCE) { this.metamerA = metamerA; this.metamerB = metamerB; this.distance = distance; this.stiffness = stiffness; Debug.Assert(!metamerA.isBroken); Debug.Assert(!metamerB.isBroken); // Square root approximations variables distanceSq = distance * distance; metamerB.relatedConstraints.Add(this); }
// assembleShoot private Metamer assembleShoot() { // Initial metamer conditions for the head metamer BudType initialActiveBud = tree.maxShootLength > 1 ? BudType.LATERAL : BudType.TERMINAL; BudState initialTerminalBudState = tree.maxShootLength > 1 ? BudState.NODE : BudState.DORMANT; BudState initialLateralBudState = BudState.DORMANT; //float initialAxis = (float)Math.Atan2(optimalGrowthDirection.Y, optimalGrowthDirection.X); // Calculate the growth direction Vector2 newGrowthDirection = new Vector2((float)Math.Cos(axis), (float)Math.Sin(axis)); newGrowthDirection += (optimalGrowthDirection * tree.optimalGrowthWeight); newGrowthDirection += (tree.tropism * tree.tropismWeight); newGrowthDirection.Normalize(); float initialAxis = (float)Math.Atan2(newGrowthDirection.Y, newGrowthDirection.X); // Calculate the shoot length int shootLength = (int)(budQuality * tree.maxShootLength); bool recalculateDistance = false; Vector2 hitPoint = Vector2.Zero; tree.treeSystem.physicsSystem.getWorld(tree.levelUid).RayCast((Fixture fixture, Vector2 point, Vector2 normal, float fraction) => { int entityId = (int)fixture.Body.UserData; if (tree.treeSystem.entityManager.getComponent(tree.levelUid, entityId, ComponentType.IgnoreTreeCollision) != null) return fraction; recalculateDistance = true; hitPoint = point; return fraction; }, position, position + newGrowthDirection * ((float)shootLength * tree.internodeLength)); if (recalculateDistance) shootLength = (int)Math.Floor((hitPoint - position).Length()); if (shootLength > 0) { Metamer head = new Metamer(tree, this, initialActiveBud, initialTerminalBudState, initialLateralBudState, initialAxis, !placeBudOnLeft); Metamer tail = head; for (int i = 1; i < shootLength; i++) { bool onLastMetamer = i == shootLength - 1; BudType subsequentActiveBud = onLastMetamer ? BudType.TERMINAL : BudType.LATERAL; BudState subsequentTerminalBudState = onLastMetamer ? BudState.DORMANT : BudState.NODE; BudState subsequentLateralBudState = BudState.DORMANT; // Add metamer onto most recently created metamer tail.mainMetamer = new Metamer( tree, tail, subsequentActiveBud, subsequentTerminalBudState, subsequentLateralBudState, initialAxis, !tail.placeBudOnLeft); // Store tail metamer tail = tail.mainMetamer; } tail.isTail = true; // Create distance constraint between this metamer and the tail constraints.Add(new DistanceMetamerConstraint(this, tail, (position - tail.position).Length(), 1f)); return head; } return null; }
// appendNewShoots public void appendNewShoots() { if (isBroken) return; // Chain metamer calls if (mainMetamer != null) mainMetamer.appendNewShoots(); if (lateralMetamer != null) lateralMetamer.appendNewShoots(); if (activeBud != BudType.NONE) { // Check bud quality if (budQuality > 0) { // Create shoots... Debug.Assert(activeBud != BudType.NONE); if (activeBud == BudType.TERMINAL) { if (terminalBudState != BudState.DEAD) { mainMetamer = assembleShoot(); terminalBudState = mainMetamer == null ? BudState.DEAD : BudState.NODE; } activeBud = BudType.LATERAL; } else if (activeBud == BudType.LATERAL) { if (lateralBudState != BudState.DEAD) { lateralMetamer = assembleShoot(); lateralBudState = lateralMetamer == null ? BudState.DEAD : BudState.NODE; } activeBud = BudType.NONE; } } } }
// kill private void kill() { string levelUid = tree.levelUid; if (mainMetamer != null) mainMetamer.kill(); if (lateralMetamer != null) lateralMetamer.kill(); // Remove from metamer grid tree.treeSystem.metamerGrid[levelUid][ci][cj].Remove(this); if (tree.treeSystem.metamerGrid[levelUid][ci][cj].Count == 0) tree.treeSystem.metamerGrid[levelUid][ci].Remove(cj); if (tree.treeSystem.metamerGrid[levelUid][ci].Count == 0) tree.treeSystem.metamerGrid[levelUid].Remove(ci); // Should have no bodies or mouse joints Debug.Assert(body == null); Debug.Assert(mouseJoint == null); // Clear connections mainMetamer = null; lateralMetamer = null; if (previousMetamer != null) { if (previousMetamer.mainMetamer == this) previousMetamer.mainMetamer = null; else if (previousMetamer.lateralMetamer == this) previousMetamer.lateralMetamer = null; } previousMetamer = null; // Clear lists constraints.Clear(); relatedConstraints.Clear(); constraintPoints.Clear(); previousConstraintPoints.Clear(); collisionNormals = null; collisionVertices = null; }
// findConstraintPoints private void findConstraintPoints(Metamer exclude, Metamer start) { float minLateralDot = 0.4f; int maxLateralConstraints = 8; int maxPreviousConstraints = 4; // Search previous path if (start.previousMetamer != null && start.previousMetamer != exclude) { Metamer current = start; bool done = false; while (!done) { // Stop searching and spawn a new constraint point search if current.previous is a branching point or tail if (current.previousMetamer != null && (current.previousMetamer.isBranchingPoint())) { done = true; previousConstraintPoints.Add(current.previousMetamer); if (previousConstraintPoints.Count < maxPreviousConstraints) findConstraintPoints(current, current.previousMetamer); } if (!done) { // Continue searching if the previous metamer exists if (current.previousMetamer != null) current = current.previousMetamer; else done = true; } } } // Search main path if (start.mainMetamer != null && start.mainMetamer != exclude) { Metamer current = start; bool done = false; while (!done) { // Stop searching and spawn a new constraint point search if current.main is a branching point or tail if (current.mainMetamer != null && (current.mainMetamer.isBranchingPoint() || current.mainMetamer.isTail)) { done = true; Vector2 relative = position - current.mainMetamer.position; relative.Normalize(); Vector2 normal = start.position - current.mainMetamer.position; normal.Normalize(); if (Math.Abs(Vector2.Dot(relative, normal)) >= minLateralDot) constraintPoints.Add(current.mainMetamer); if (constraintPoints.Count < maxLateralConstraints) findConstraintPoints(current, current.mainMetamer); } if (!done) { // Continue searching if the previous metamer exists if (current.mainMetamer != null) current = current.mainMetamer; else done = true; } } } // Search lateral path if (start.lateralMetamer != null && start.lateralMetamer != exclude) { Metamer current = start; bool done = false; while (!done) { // Stop searching and spawn a new constraint point search if current.main is a branching point or tail if (current.lateralMetamer != null && (current.lateralMetamer.isBranchingPoint() || current.lateralMetamer.isTail)) { done = true; Vector2 relative = position - current.lateralMetamer.position; relative.Normalize(); Vector2 normal = start.position - current.lateralMetamer.position; normal.Normalize(); if (Math.Abs(Vector2.Dot(relative, normal)) >= minLateralDot) constraintPoints.Add(current.lateralMetamer); if (constraintPoints.Count < maxLateralConstraints) findConstraintPoints(current, current.lateralMetamer); } if (!done) { // Continue searching if the previous metamer exists if (current.lateralMetamer != null) current = current.lateralMetamer; else done = true; } } } }
public Metamer(Tree tree, Metamer previousMetamer, BudType activeBud, BudState terminalBudState, BudState lateralBudState, float axis, bool placeBudOnLeft) { this.tree = tree; this.activeBud = activeBud; this.previousMetamer = previousMetamer; this.terminalBudState = terminalBudState; this.lateralBudState = lateralBudState; this.axis = axis; this.placeBudOnLeft = placeBudOnLeft; aabb = new AABB(); isRoot = previousMetamer == null; constraintPoints = new List<Metamer>(); previousConstraintPoints = new List<Metamer>(); associatedMarkers = new List<MetamerMarker>(); fixturesToTest = new Fixture[MAX_FIXTURES_TO_TEST]; localGridHalfWidth = (int)Math.Floor(tree.perceptionRadius / TreeSystem.PLANT_CELL_SIZE) + 2; localGridHalfHeight = (int)Math.Floor(tree.perceptionRadius / TreeSystem.PLANT_CELL_SIZE) + 2; currentAngle = axis; currentTextureAngle = axis; collisionVertices = new Vector2[FarseerPhysics.Settings.MaxPolygonVertices]; collisionNormals = new Vector2[FarseerPhysics.Settings.MaxPolygonVertices]; constraints = new List<MetamerConstraint>(); relatedConstraints = new List<MetamerConstraint>(); _vertices = new VertexPositionColorTexture[2]; // Mass mass = 1f; inverseMass = 1f / mass; // Calculate position position = (isRoot ? tree.position : previousMetamer.position + new Vector2((float)Math.Cos(axis), (float)Math.Sin(axis)) * tree.internodeLength); oldPosition = position; // Trunk constraints if (!isRoot) { if (isApex() && tree.iterations < 2) { // Create anchor constraints Vector2 relative = position - tree.rootPosition; Vector2 anchorA = tree.rootPosition + tree.anchorNormal * 3f; float distance = (anchorA - position).Length(); constraints.Add(new TrunkMetamerConstraint(this, anchorA, distance)); Vector2 anchorB = tree.rootPosition + tree.anchorNormal * -3f; distance = (anchorB - position).Length(); constraints.Add(new TrunkMetamerConstraint(this, anchorB, distance)); } // Create metamer-metamer links if (!previousMetamer.isBroken) internodeConstraint = new DistanceMetamerConstraint(this, previousMetamer, tree.internodeLength * 0.8f, 0.5f); } // Put this metamer in a cell in the metamer grid ci = tree.treeSystem.getPlantGridX(position.X); cj = tree.treeSystem.getPlantGridY(position.Y); if (!tree.treeSystem.metamerGrid.ContainsKey(tree.levelUid)) { tree.treeSystem.metamerGrid.Add(tree.levelUid, new Dictionary<int, Dictionary<int, List<Metamer>>>()); } if (!tree.treeSystem.metamerGrid[tree.levelUid].ContainsKey(ci)) { tree.treeSystem.metamerGrid[tree.levelUid][ci] = new Dictionary<int, List<Metamer>>(); } if (!tree.treeSystem.metamerGrid[tree.levelUid][ci].ContainsKey(cj)) { tree.treeSystem.metamerGrid[tree.levelUid][ci][cj] = new List<Metamer>(); } tree.treeSystem.metamerGrid[tree.levelUid][ci][cj].Add(this); // Destroy markers in the occupied zone destroyMarkersInOccupiedZone(); // Expand level boundary tree.treeSystem.levelSystem.expandFallbackBoundary(tree.levelUid, position); // Determine z-index _z = tree.layerDepth + StasisMathHelper.floatBetween(-0.02f, 0.01f, tree.random); }
// Constructor public Tree(TreeSystem treeSystem, string levelUid, Texture2D barkTexture, List<List<Texture2D>> leafTextures, XElement data) { _treeSystem = treeSystem; _levelUid = levelUid; _leafTextures = leafTextures; _barkTexture = barkTexture; _angle = Loader.loadFloat(data.Attribute("angle"), 0f); _seed = Loader.loadInt(data.Attribute("seed"), 12345); _age = Loader.loadFloat(data.Attribute("age"), 0f); _internodeHalfLength = Loader.loadFloat(data.Attribute("internode_half_length"), 0.5f); _internodeLength = _internodeHalfLength * 2f; _maxShootLength = Loader.loadInt(data.Attribute("max_shoot_length"), 4); _maxBaseHalfWidth = Loader.loadFloat(data.Attribute("max_base_half_width"), 0.25f); _perceptionAngle = Loader.loadFloat(data.Attribute("perception_angle"), 0.6f); _perceptionRadius = Loader.loadFloat(data.Attribute("perception_radius"), 4f); _occupancyRadius = Loader.loadFloat(data.Attribute("occupancy_radius"), 1f); _lateralAngle = Loader.loadFloat(data.Attribute("lateral_angle"), 0.6f); _fullExposure = Loader.loadFloat(data.Attribute("full_exposure"), 1f); _penumbraA = Loader.loadFloat(data.Attribute("penumbra_a"), 1f); _penumbraB = Loader.loadFloat(data.Attribute("penumbra_b"), 2f); _optimalGrowthWeight = Loader.loadFloat(data.Attribute("optimal_growth_weight"), 1f); _tropismWeight = Loader.loadFloat(data.Attribute("tropism_weight"), 1f); _tropism = Loader.loadVector2(data.Attribute("tropism"), Vector2.Zero); _position = Loader.loadVector2(data.Attribute("position"), Vector2.Zero); _layerDepth = Loader.loadFloat(data.Attribute("layer_depth"), 0.1f); _entityId = int.Parse(data.Attribute("id").Value); _vertices = new VertexPositionColorTexture[MAX_VERTICES]; for (int i = 0; i < MAX_VERTICES; i++) { _vertices[i].Color = Color.White; } _random = new Random(_seed); _internodeLengthSq = _internodeLength * _internodeLength; _aabb = new AABB(); _aabb.LowerBound = _position; _aabb.UpperBound = _position; // Calculate root position float rootAngle = _angle + (StasisMathHelper.pi); _rootPosition = _position + new Vector2((float)Math.Cos(rootAngle), (float)Math.Sin(rootAngle)) * 5f; // Calculate anchor normals float anchorAngle = _angle - (StasisMathHelper.pi * 0.5f); _anchorNormal = new Vector2((float)Math.Cos(anchorAngle), (float)Math.Sin(anchorAngle)); // Create first metamer _rootMetamer = new Metamer(this, null, BudType.TERMINAL, BudState.DORMANT, BudState.DEAD, _angle, true); _rootMetamer.isTail = true; }