/**
  * reads a list of ShaderNode{} blocks
  *
  * @param statements the list of statements to parse
  * @throws IOException
  */
 protected void readShaderNode(List<Statement> statements) throws IOException {
   for (Statement statement : statements) {
     String line = statement.getLine();
     String[] split = statement.getLine().split("[ \\{]");
     if (line.startsWith("Definition")) {
       ShaderNodeDefinition def = findDefinition(statement);
       shaderNode.setDefinition(def);
       if (def.isNoOutput()) {
         techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName());
       }
     } else if (line.startsWith("Condition")) {
       String condition = line.substring(line.lastIndexOf(":") + 1).trim();
       extractCondition(condition, statement);
       shaderNode.setCondition(conditionParser.getFormattedExpression());
     } else if (line.startsWith("InputMapping")) {
       for (Statement statement1 : statement.getContents()) {
         VariableMapping mapping = readInputMapping(statement1);
         techniqueDef
             .getShaderGenerationInfo()
             .getUnusedNodes()
             .remove(mapping.getRightVariable().getNameSpace());
         shaderNode.getInputMapping().add(mapping);
       }
     } else if (line.startsWith("OutputMapping")) {
       for (Statement statement1 : statement.getContents()) {
         VariableMapping mapping = readOutputMapping(statement1);
         techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName());
         shaderNode.getOutputMapping().add(mapping);
       }
     } else {
       throw new MatParseException("ShaderNodeDefinition", split[0], statement);
     }
   }
 }
  /**
   * Reads alist of ShaderNodes
   *
   * @param statements the list of statements to read
   * @throws IOException
   */
  public void readNodes(List<Statement> statements) throws IOException {
    if (techniqueDef.getShaderNodes() == null) {
      techniqueDef.setShaderNodes(new ArrayList<ShaderNode>());
      techniqueDef.setShaderGenerationInfo(new ShaderGenerationInfo());
    }

    for (Statement statement : statements) {
      String[] split = statement.getLine().split("[ \\{]");
      if (statement.getLine().startsWith("ShaderNode ")) {
        String name = statement.getLine().substring("ShaderNode".length()).trim();
        if (nodes == null) {
          nodes = new HashMap<String, ShaderNode>();
        }
        if (!nodes.containsKey(name)) {
          shaderNode = new ShaderNode();
          shaderNode.setName(name);
          techniqueDef.getShaderGenerationInfo().getUnusedNodes().add(name);

          readShaderNode(statement.getContents());
          nodes.put(name, shaderNode);
          techniqueDef.getShaderNodes().add(shaderNode);
        } else {
          throw new MatParseException("ShaderNode " + name + " is already defined", statement);
        }

      } else {
        throw new MatParseException("ShaderNode", split[0], statement);
      }
    }
  }
 /**
  * stores a global output
  *
  * @param var the variable to store
  * @param statement1 the statement being read
  * @throws IOException
  */
 public void storeGlobal(ShaderNodeVariable var, Statement statement1) throws IOException {
   var.setShaderOutput(true);
   if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
     ShaderNodeVariable global = techniqueDef.getShaderGenerationInfo().getVertexGlobal();
     if (global != null) {
       //                global.setCondition(mergeConditions(global.getCondition(),
       // var.getCondition(), "||"));
       //                var.setCondition(global.getCondition());
       if (!global.getName().equals(var.getName())) {
         throw new MatParseException(
             "A global output is already defined for the vertex shader: "
                 + global.getName()
                 + ". vertex shader can only have one global output",
             statement1);
       }
     } else {
       techniqueDef.getShaderGenerationInfo().setVertexGlobal(var);
     }
   } else if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
     storeVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentGlobals());
   }
 }
  /**
   * store a varying
   *
   * @param node the shaderNode
   * @param variable the variable to store
   */
  public void storeVaryings(ShaderNode node, ShaderNodeVariable variable) {
    variable.setShaderOutput(true);
    if (node.getDefinition().getType() == Shader.ShaderType.Vertex
        && shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
      DeclaredVariable dv = varyings.get(variable.getName());
      if (dv == null) {
        techniqueDef.getShaderGenerationInfo().getVaryings().add(variable);
        dv = new DeclaredVariable(variable);

        varyings.put(variable.getName(), dv);
      }
      dv.addNode(shaderNode);
      // if a variable is declared with the same name as an input and an output and is a varying,
      // set it as a shader output so it's declared as a varying only once.
      for (VariableMapping variableMapping : node.getInputMapping()) {
        if (variableMapping.getLeftVariable().getName().equals(variable.getName())) {
          variableMapping.getLeftVariable().setShaderOutput(true);
        }
      }
    }
  }
 /**
  * store a fragment uniform
  *
  * @param var the variable ot store
  */
 public void storeFragmentUniform(ShaderNodeVariable var) {
   storeVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentUniforms());
 }
 /**
  * store an attribute
  *
  * @param var the variable ot store
  */
 public void storeAttribute(ShaderNodeVariable var) {
   storeVariable(var, techniqueDef.getShaderGenerationInfo().getAttributes());
 }