private void AppendBranch(Matrix transform, int parentId, float lengthScale, float radiusScale, int level) { if (level <= 0) { return; // Do nothing, this branch is invisible. } Branch branch = branches[parentId]; // Add this branch float length = lengthScale * GetFloatFromValueRange(branch.length, seed++); float radius = GetFloatFromValueRange(branch.radius, seed++); float radiusEndScale = radiusScale * GetFloatFromValueRange(branch.radiusEnd, seed++); float radiusEnd = radius * radiusEndScale; radius = radiusScale * radius; if (level == 1) { radiusEnd = 0.0f; } int radialSegments = (level * (this.radialSegments - 3)) / levels + 3; if (level > cutoffLevel) { MeshUtil.BuildCylinder( treeVertices, treeIndices, ref boundingBox, length, radius, radiusEnd, radialSegments, transform, 0.0f, 1.0f); } // Add children if (level > 1) { for (int childN = 0; childN < branch.children.Count; childN++) { BranchChild child = branch.children[childN]; int childBranchId = GetBranchIndexFromId(child.idRef); if (childBranchId == -1) { continue; } Branch childBranch = branches[childBranchId]; if (!IsValueInRange(child.levelRange, level)) { continue; } int childLevel = level + GetIntFromValueRange(child.relativeLevel, seed++); // RelativeLevel is usually negative, -1 in particular. int numChildren = GetIntFromValueRange(child.count, seed++); float positionRange = child.position.max - child.position.min; positionRange /= (float)numChildren; float orientation = GetRandomFloat(seed++, 0, 360.0f); for (int i = 0; i < numChildren; i++) { float childLengthScale = lengthScale * GetFloatFromValueRange(child.lengthScale, seed++); orientation += GetFloatFromValueRange(child.orientation, seed++); // Clamp value between 0 and 360. if (orientation < 0.0f) { orientation += 360.0f; } else if (orientation > 360.0f) { orientation -= 360.0f; } float childOrientation = orientation; float gravity = GetFloatFromValueRange(child.gravityInfluence, seed++); float childPitch = GetFloatFromValueRange(child.pitch, seed++); float childPosition = GetFloatFromValueRange(child.position, seed++); float position; if (child.position.isFixedValue) { position = child.position.min; } else { position = (child.position.min + positionRange * i + positionRange * GetRandomFloat(seed++, 0f, 1f)); } float childRadiusScale = (radiusScale * (1.0f - position) + radiusEndScale * position) * GetFloatFromValueRange(child.radiusScale, seed++); // Build transformation matrix Matrix mat = Matrix.CreateRotationX(MathHelper.ToRadians(childPitch)) * Matrix.CreateRotationY(MathHelper.ToRadians(childOrientation)); // Set the Y translation mat.M42 = length * position; // Transform by the branch transformation mat = mat * transform; if (gravity != 0.0f) { // Do some extra work // Get a vector pointing downwards (towards gravity's pull) Vector3 vDown = -Vector3.UnitY; // Get a vector pointing in the branch's direction, by rotating the Y unit vector. Vector3 vBranch = mat.Up; Vector3 vSide; if (Math.Abs(vBranch.Y) >= 0.9f) { // The X unit vector should suffice as side vector if rotated by the matrix. vSide = mat.Right; //Vector3.TransformNormal(Vector3.UnitX, mat); } else { // Use the cross product to find a suitable sideways vector vSide = Vector3.Cross(vBranch, vDown); vSide.Normalize(); } vDown = Vector3.Cross(vSide, vBranch); vDown.Normalize(); mat.Right = vSide; mat.Backward = vDown; float dot = -vBranch.Y; if (gravity < 0.0f) { dot = -dot; } float angle = (float)Math.Acos(dot); angle *= gravity; // Bend the branch around the X-axis. Matrix mat2 = Matrix.CreateRotationX(angle); mat = mat2 * mat; } // Add the branch AppendBranch(mat, childBranchId, childLengthScale, childRadiusScale, childLevel); } } } // Add leaves if (addLeaves) { for (int leafN = 0; leafN < branch.leaves.Count; leafN++) { Leaf leaf = branch.leaves[leafN]; if (!IsValueInRange(leaf.levelRange, level)) { continue; } int count = GetIntFromValueRange(leaf.count, leafSeed++); for (int i = 0; i < count; i++) { float width = GetFloatFromValueRange(leaf.width, leafSeed++); float height = GetFloatFromValueRange(leaf.height, leafSeed++); float scale = GetFloatFromValueRange(leaf.scale, leafSeed++); float position = GetFloatFromValueRange(leaf.position, leafSeed++); float roll = GetFloatFromValueRange(leaf.roll, leafSeed++); float anchor = GetFloatFromValueRange(leaf.anchor, leafSeed++); Matrix mat = Matrix.Identity; mat.M42 = length * position; mat = mat * transform; Vector3 leafPos = mat.Translation; Color color = new Color( (byte)GetIntFromValueRange(leaf.red, leafSeed++), (byte)GetIntFromValueRange(leaf.green, leafSeed++), (byte)GetIntFromValueRange(leaf.blue, leafSeed++), (byte)GetIntFromValueRange(leaf.alpha, leafSeed++)); //Console.WriteLine("Color = " + color.R + ", " + color.G + ", " + color.B + ", " + color.A); if (leaf.hasAxis) { cloud.AxisEnabled = true; cloud.Axis = leaf.axis; leafPos += leaf.axis * height * scale * anchor / 2.0f; } cloud.AddParticle(leafPos, color, roll, new Vector2(width, height)); } } } }
private bool LoadXmlBranchChild(XmlNode node, List <BranchChild> children) { BranchChild child = BranchChild.Create(); child.idRef = GetIntAttribute(node, "ref", -1); // Must have a valid branch reference id if (child.idRef < 0) { return(false); } // Get the relative level short-hand from the attribute (if available) AssignValueRange(node, "level", ref child.relativeLevel); // Let all subelements apply to the new branch type for (int i = 0; i < node.ChildNodes.Count; i++) { XmlNode childnode = node.ChildNodes[i]; switch (childnode.Name) { case "relativeLevel": AssignValueRange(childnode, ref child.relativeLevel); break; case "levelRange": AssignValueRange(childnode, ref child.levelRange); break; case "position": AssignValueRange(childnode, ref child.position); break; case "lengthScale": AssignValueRange(childnode, ref child.lengthScale); break; case "radiusScale": AssignValueRange(childnode, ref child.radiusScale); break; case "orientation": AssignValueRange(childnode, ref child.orientation); break; case "pitch": AssignValueRange(childnode, ref child.pitch); break; case "gravity": AssignValueRange(childnode, ref child.gravityInfluence); break; case "count": AssignValueRange(childnode, ref child.count); break; } } // Add the branchchild to the array children.Add(child); return(true); }