protected void initNodesLoader() {
   if (!isUseNodes) {
     isUseNodes =
         shaderNames.get(Shader.ShaderType.Vertex) == null
             && shaderNames.get(Shader.ShaderType.Fragment) == null;
     if (isUseNodes) {
       if (nodesLoaderDelegate == null) {
         nodesLoaderDelegate = new ShaderNodeLoaderDelegate();
       } else {
         nodesLoaderDelegate.clear();
       }
       nodesLoaderDelegate.setTechniqueDef(technique);
       nodesLoaderDelegate.setMaterialDef(materialDef);
       nodesLoaderDelegate.setAssetManager(assetManager);
     }
   }
 }
 private void readTechniqueStatement(Statement statement) throws IOException {
   String[] split = statement.getLine().split("[ \\{]");
   if (split[0].equals("VertexShader")
       || split[0].equals("FragmentShader")
       || split[0].equals("GeometryShader")
       || split[0].equals("TessellationControlShader")
       || split[0].equals("TessellationEvaluationShader")) {
     readShaderStatement(statement.getLine());
   } else if (split[0].equals("LightMode")) {
     readLightMode(statement.getLine());
   } else if (split[0].equals("LightSpace")) {
     readLightSpace(statement.getLine());
   } else if (split[0].equals("ShadowMode")) {
     readShadowMode(statement.getLine());
   } else if (split[0].equals("WorldParameters")) {
     readWorldParams(statement.getContents());
   } else if (split[0].equals("RenderState")) {
     readRenderState(statement.getContents());
   } else if (split[0].equals("ForcedRenderState")) {
     readForcedRenderState(statement.getContents());
   } else if (split[0].equals("Defines")) {
     readDefines(statement.getContents());
   } else if (split[0].equals("ShaderNodesDefinitions")) {
     initNodesLoader();
     if (isUseNodes) {
       nodesLoaderDelegate.readNodesDefinitions(statement.getContents());
     }
   } else if (split[0].equals("VertexShaderNodes")) {
     initNodesLoader();
     if (isUseNodes) {
       nodesLoaderDelegate.readVertexShaderNodes(statement.getContents());
     }
   } else if (split[0].equals("FragmentShaderNodes")) {
     initNodesLoader();
     if (isUseNodes) {
       nodesLoaderDelegate.readFragmentShaderNodes(statement.getContents());
     }
   } else if (split[0].equals("NoRender")) {
     technique.setNoRender(true);
   } else {
     throw new MatParseException(null, split[0], statement);
   }
 }
  private void readTechnique(Statement techStat) throws IOException {
    isUseNodes = false;
    String[] split = techStat.getLine().split(whitespacePattern);

    String name;
    if (split.length == 1) {
      name = TechniqueDef.DEFAULT_TECHNIQUE_NAME;
    } else if (split.length == 2) {
      name = split[1];
    } else {
      throw new IOException("Technique statement syntax incorrect");
    }

    String techniqueUniqueName = materialDef.getAssetName() + "@" + name;
    technique = new TechniqueDef(name, techniqueUniqueName.hashCode());

    for (Statement statement : techStat.getContents()) {
      readTechniqueStatement(statement);
    }

    technique.setShaderPrologue(createShaderPrologue(presetDefines));

    switch (technique.getLightMode()) {
      case Disable:
        technique.setLogic(new DefaultTechniqueDefLogic(technique));
        break;
      case MultiPass:
        technique.setLogic(new MultiPassLightingLogic(technique));
        break;
      case SinglePass:
        technique.setLogic(new SinglePassLightingLogic(technique));
        break;
      case StaticPass:
        technique.setLogic(new StaticPassLightingLogic(technique));
        break;
      case SinglePassAndImageBased:
        technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique));
        break;
      default:
        throw new UnsupportedOperationException();
    }

    List<TechniqueDef> techniqueDefs = new ArrayList<>();

    if (isUseNodes) {
      nodesLoaderDelegate.computeConditions();

      // used for caching later, the shader here is not a file.

      // KIRILL 9/19/2015
      // Not sure if this is needed anymore, since shader caching
      // is now done by TechniqueDef.
      technique.setShaderFile(
          technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
      techniqueDefs.add(technique);
    } else if (shaderNames.containsKey(Shader.ShaderType.Vertex)
        && shaderNames.containsKey(Shader.ShaderType.Fragment)) {
      if (shaderLanguages.size() > 1) {
        for (int i = 1; i < shaderLanguages.size(); i++) {
          TechniqueDef td = null;
          try {
            td = (TechniqueDef) technique.clone();
          } catch (CloneNotSupportedException e) {
            e.printStackTrace();
          }
          td.setShaderFile(shaderNames, shaderLanguages.get(i));
          techniqueDefs.add(td);
        }
      }
      technique.setShaderFile(shaderNames, shaderLanguages.get(0));
      techniqueDefs.add(technique);

    } else {
      technique = null;
      shaderLanguages.clear();
      shaderNames.clear();
      presetDefines.clear();
      langSize = 0;
      logger.log(Level.WARNING, "Fixed function technique was ignored");
      logger.log(
          Level.WARNING,
          "Fixed function technique ''{0}'' was ignored for material {1}",
          new Object[] {name, key});
      return;
    }

    for (TechniqueDef techniqueDef : techniqueDefs) {
      materialDef.addTechniqueDef(techniqueDef);
    }

    technique = null;
    langSize = 0;
    shaderLanguages.clear();
    shaderNames.clear();
    presetDefines.clear();
  }