/// <summary> /// Parses each child of <code>node</code> as an instruction, adds them to the specified instruction list. Calls are resolved to production lists /// using the specified multimap. /// </summary> private static void ParseInstructionsFromXml(XmlNode node, List <TreeCrayonInstruction> instructions, MultiMap <string, ProductionNodePair> map) { foreach (XmlNode child in node.ChildNodes) { switch (child.Name) { case "Call": String name = XmlUtil.GetString(child, "ref"); List <Production> productions = GetProductionsByRef(name, map); instructions.Add(new Call(productions, XmlUtil.GetInt(child, "delta", -1))); break; case "Child": var ch = new Child(); ParseInstructionsFromXml(child, ch.Instructions, map); instructions.Add(ch); break; case "Maybe": var maybe = new Maybe(XmlUtil.GetFloat(child, "chance", 0.50f)); ParseInstructionsFromXml(child, maybe.Instructions, map); instructions.Add(maybe); break; case "Forward": instructions.Add(new Forward(XmlUtil.GetFloat(child, "distance"), XmlUtil.GetFloat(child, "variation", 0.0f), XmlUtil.GetFloat(child, "radius", 0.86f))); break; case "Backward": instructions.Add(new Backward(XmlUtil.GetFloat(child, "distance"), XmlUtil.GetFloat(child, "variation", 0.0f))); break; case "Pitch": instructions.Add(new Pitch(XmlUtil.GetFloat(child, "angle"), XmlUtil.GetFloat(child, "variation", 0.0f))); break; case "Scale": instructions.Add(new Scale(XmlUtil.GetFloat(child, "scale"), XmlUtil.GetFloat(child, "variation", 0.0f))); break; case "ScaleRadius": instructions.Add(new ScaleRadius(XmlUtil.GetFloat(child, "scale"), XmlUtil.GetFloat(child, "variation", 0.0f))); break; case "Twist": instructions.Add(new Twist(XmlUtil.GetFloat(child, "angle", 0), XmlUtil.GetFloat(child, "variation", 360.0f))); break; case "Level": instructions.Add(new Level(XmlUtil.GetInt(child, "delta", -1))); break; case "Leaf": instructions.Add(ParseLeafFromXml(child)); break; case "Bone": instructions.Add(new Bone(XmlUtil.GetInt(child, "delta", -1))); break; case "RequireLevel": String type = XmlUtil.GetStringOrNull(child, "type"); CompareType ctype = type == "less" ? CompareType.Less : CompareType.Greater; var req = new RequireLevel(XmlUtil.GetInt(child, "level"), ctype); ParseInstructionsFromXml(child, req.Instructions, map); instructions.Add(req); break; case "Align": instructions.Add(new Align()); break; } } }
/// <summary> /// Creates a tree generator from a specified XML document. /// </summary> /// <param name="document">The XML document to parse.</param> /// <returns>A new tree generator.</returns> /// <exception cref="ArgumentException">If the XML document is not a valid tree specification.</exception> public static TreeGenerator CreateFromXml(XmlDocument document) { var generator = new TreeGenerator(); string rootName = null; int levels = -1; int boneLevels = 3; var productions = new MultiMap <string, ProductionNodePair>(); XmlNode root = document.SelectSingleNode("Tree"); foreach (XmlNode child in root.ChildNodes) { switch (child.Name) { case "Root": rootName = XmlUtil.GetString(child, "ref"); break; case "Levels": levels = XmlUtil.GetInt(child, "value"); break; case "BoneLevels": boneLevels = XmlUtil.GetInt(child, "value"); break; case "LeafAxis": generator.LeafAxis = XmlUtil.GetVector3(child, "value"); generator.LeafAxis.Value.Normalize(); break; case "Production": string name = XmlUtil.GetString(child, "id"); productions.Add(name, new ProductionNodePair(new Production(), child)); break; case "ConstrainUnderground": generator.Constraints.Constaints.Add( new ConstrainUndergroundBranches(XmlUtil.GetFloat(child, "lowerBound", 256.0f))); break; case "TextureHeight": generator.TextureHeight = XmlUtil.GetFloat(child, "height"); generator.TextureHeightVariation = XmlUtil.GetFloat(child, "variation", 0.0f); break; } } if (rootName == null) { throw new ArgumentException("Root name must be specified."); } // Now we have a map of names -> productions, so we can start parsing the the productions foreach (ProductionNodePair pn in productions.Values) { ParseInstructionsFromXml(pn.Node, pn.Production.Instructions, productions); } generator.Root = productions[rootName][0].Production; generator.MaxLevel = levels; generator.BoneLevels = boneLevels; return(generator); }