Пример #1
0
  /**
   * Preloads this material for the given render manager.
   *
   * <p>Preloading the material can ensure that when the material is first used for rendering, there
   * won't be any delay since the material has been already been setup for rendering.
   *
   * @param rm The render manager to preload for
   */
  public void preload(RenderManager rm) {
    autoSelectTechnique(rm);

    Renderer r = rm.getRenderer();
    TechniqueDef techDef = technique.getDef();

    Collection<MatParam> params = paramValues.values();
    for (MatParam param : params) {
      if (param instanceof MatParamTexture) {
        MatParamTexture texParam = (MatParamTexture) param;
        r.setTexture(0, texParam.getTextureValue());
      } else {
        if (!techDef.isUsingShaders()) {
          continue;
        }

        technique.updateUniformParam(param.getName(), param.getVarType(), param.getValue());
      }
    }

    Shader shader = technique.getShader();
    if (techDef.isUsingShaders()) {
      r.setShader(shader);
    }
  }
Пример #2
0
  /**
   * Clear a parameter from this material. The parameter must exist
   *
   * @param name the name of the parameter to clear
   */
  public void clearParam(String name) {
    checkSetParam(null, name);
    MatParam matParam = getParam(name);
    if (matParam == null) {
      return;
    }

    paramValues.remove(name);
    if (matParam instanceof MatParamTexture) {
      int texUnit = ((MatParamTexture) matParam).getUnit();
      nextTexUnit--;
      for (MatParam param : paramValues.values()) {
        if (param instanceof MatParamTexture) {
          MatParamTexture texParam = (MatParamTexture) param;
          if (texParam.getUnit() > texUnit) {
            texParam.setUnit(texParam.getUnit() - 1);
          }
        }
      }
      sortingId = -1;
    }
    if (technique != null) {
      technique.notifyParamChanged(name, null, null);
    }
  }
Пример #3
0
 private void autoSelectTechnique(RenderManager rm) {
   if (technique == null) {
     selectTechnique("Default", rm);
   } else {
     technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps());
   }
 }
Пример #4
0
 /**
  * Returns the sorting ID or sorting index for this material.
  *
  * <p>The sorting ID is used internally by the system to sort rendering of geometries. It sorted
  * to reduce shader switches, if the shaders are equal, then it is sorted by textures.
  *
  * @return The sorting ID used for sorting geometries for rendering.
  */
 public int getSortId() {
   Technique t = getActiveTechnique();
   if (sortingId == -1 && t != null && t.getShader() != null) {
     int texId = -1;
     for (int i = 0; i < paramValues.size(); i++) {
       MatParam param = paramValues.getValue(i);
       if (param instanceof MatParamTexture) {
         MatParamTexture tex = (MatParamTexture) param;
         if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) {
           if (texId == -1) {
             texId = 0;
           }
           texId += tex.getTextureValue().getImage().getId() % 0xff;
         }
       }
     }
     sortingId = texId + t.getShader().getId() * 1000;
   }
   return sortingId;
 }
Пример #5
0
  /**
   * Pass a parameter to the material shader.
   *
   * @param name the name of the parameter defined in the material definition (j3md)
   * @param type the type of the parameter {@link VarType}
   * @param value the value of the parameter
   */
  public void setParam(String name, VarType type, Object value) {
    checkSetParam(type, name);

    if (type.isTextureType()) {
      setTextureParam(name, type, (Texture) value);
    } else {
      MatParam val = getParam(name);
      if (val == null) {
        MatParam paramDef = def.getMaterialParam(name);
        paramValues.put(name, new MatParam(type, name, value, paramDef.getFixedFuncBinding()));
      } else {
        val.setValue(value);
      }

      if (technique != null) {
        technique.notifyParamChanged(name, type, value);
      }
    }
  }
Пример #6
0
  /**
   * Set a texture parameter.
   *
   * @param name The name of the parameter
   * @param type The variable type {@link VarType}
   * @param value The texture value of the parameter.
   * @throws IllegalArgumentException is value is null
   */
  public void setTextureParam(String name, VarType type, Texture value) {
    if (value == null) {
      throw new IllegalArgumentException();
    }

    checkSetParam(type, name);
    MatParamTexture val = getTextureParam(name);
    if (val == null) {
      paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++));
    } else {
      val.setTextureValue(value);
    }

    if (technique != null) {
      technique.notifyParamChanged(name, type, nextTexUnit - 1);
    }

    // need to recompute sort ID
    sortingId = -1;
  }
Пример #7
0
  /**
   * Select the technique to use for rendering this material.
   *
   * <p>If <code>name</code> is "Default", then one of the {@link MaterialDef#getDefaultTechniques()
   * default techniques} on the material will be selected. Otherwise, the named technique will be
   * found in the material definition.
   *
   * <p>Any candidate technique for selection (either default or named) must be verified to be
   * compatible with the system, for that, the <code>renderManager</code> is queried for
   * capabilities.
   *
   * @param name The name of the technique to select, pass "Default" to select one of the default
   *     techniques.
   * @param renderManager The {@link RenderManager render manager} to query for capabilities.
   * @throws IllegalArgumentException If "Default" is passed and no default techniques are available
   *     on the material definition, or if a name is passed but there's no technique by that name.
   * @throws UnsupportedOperationException If no candidate technique supports the system
   *     capabilities.
   */
  public void selectTechnique(String name, RenderManager renderManager) {
    // check if already created
    Technique tech = techniques.get(name);
    // When choosing technique, we choose one that
    // supports all the caps.
    EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
    if (tech == null) {

      if (name.equals("Default")) {
        List<TechniqueDef> techDefs = def.getDefaultTechniques();
        if (techDefs == null || techDefs.isEmpty()) {
          throw new IllegalArgumentException(
              "No default techniques are available on material '" + def.getName() + "'");
        }

        TechniqueDef lastTech = null;
        for (TechniqueDef techDef : techDefs) {
          if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
            // use the first one that supports all the caps
            tech = new Technique(this, techDef);
            techniques.put(name, tech);
            break;
          }
          lastTech = techDef;
        }
        if (tech == null) {
          throw new UnsupportedOperationException(
              "No default technique on material '"
                  + def.getName()
                  + "'\n"
                  + " is supported by the video hardware. The caps "
                  + lastTech.getRequiredCaps()
                  + " are required.");
        }

      } else {
        // create "special" technique instance
        TechniqueDef techDef = def.getTechniqueDef(name);
        if (techDef == null) {
          throw new IllegalArgumentException(
              "For material " + def.getName() + ", technique not found: " + name);
        }

        if (!rendererCaps.containsAll(techDef.getRequiredCaps())) {
          throw new UnsupportedOperationException(
              "The explicitly chosen technique '"
                  + name
                  + "' on material '"
                  + def.getName()
                  + "'\n"
                  + "requires caps "
                  + techDef.getRequiredCaps()
                  + " which are not "
                  + "supported by the video renderer");
        }

        tech = new Technique(this, techDef);
        techniques.put(name, tech);
      }
    } else if (technique == tech) {
      // attempting to switch to an already
      // active technique.
      return;
    }

    technique = tech;
    tech.makeCurrent(def.getAssetManager(), true, rendererCaps);

    // shader was changed
    sortingId = -1;
  }
Пример #8
0
  /**
   * Called by {@link RenderManager} to render the geometry by using this material.
   *
   * <p>The material is rendered as follows:
   *
   * <ul>
   *   <li>Determine which technique to use to render the material - either what the user selected
   *       via {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
   *       Material.selectTechnique()}, or the first default technique that the renderer supports
   *       (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering
   *       capabilities})
   *       <ul>
   *         <li>If the technique has been changed since the last frame, then it is notified via
   *             {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean,
   *             java.util.EnumSet) Technique.makeCurrent()}. If the technique wants to use a shader
   *             to render the model, it should load it at this part - the shader should have all
   *             the proper defines as declared in the technique definition, including those that
   *             are bound to material parameters. The technique can re-use the shader from the last
   *             frame if no changes to the defines occurred.
   *       </ul>
   *   <li>Set the {@link RenderState} to use for rendering. The render states are applied in this
   *       order (later RenderStates override earlier RenderStates):
   *       <ol>
   *         <li>{@link TechniqueDef#getRenderState() Technique Definition's RenderState} - i.e.
   *             specific renderstate that is required for the shader.
   *         <li>{@link #getAdditionalRenderState() Material Instance Additional RenderState} - i.e.
   *             ad-hoc renderstate set per model
   *         <li>{@link RenderManager#getForcedRenderState() RenderManager's Forced RenderState} -
   *             i.e. renderstate requested by a {@link com.jme3.post.SceneProcessor} or
   *             post-processing filter.
   *       </ol>
   *   <li>If the technique {@link TechniqueDef#isUsingShaders() uses a shader}, then the uniforms
   *       of the shader must be updated.
   *       <ul>
   *         <li>Uniforms bound to material parameters are updated based on the current material
   *             parameter values.
   *         <li>Uniforms bound to world parameters are updated from the RenderManager. Internally
   *             {@link UniformBindingManager} is used for this task.
   *         <li>Uniforms bound to textures will cause the texture to be uploaded as necessary. The
   *             uniform is set to the texture unit where the texture is bound.
   *       </ul>
   *   <li>If the technique uses a shader, the model is then rendered according to the lighting mode
   *       specified on the technique definition.
   *       <ul>
   *         <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light
   *             uniform arrays with the first 4 lights and renders the model once.
   *         <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model
   *             multiple times, for the first light it is rendered opaque, on subsequent lights it
   *             is rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth
   *             writing disabled.
   *       </ul>
   *   <li>For techniques that do not use shaders, fixed function OpenGL is used to render the model
   *       (see {@link GL1Renderer} interface):
   *       <ul>
   *         <li>OpenGL state ({@link FixedFuncBinding}) that is bound to material parameters is
   *             updated.
   *         <li>The texture set on the material is uploaded and bound. Currently only 1 texture is
   *             supported for fixed function techniques.
   *         <li>If the technique uses lighting, then OpenGL lighting state is updated based on the
   *             light list on the geometry, otherwise OpenGL lighting is disabled.
   *         <li>The mesh is uploaded and rendered.
   *       </ul>
   * </ul>
   *
   * @param geom The geometry to render
   * @param rm The render manager requesting the rendering
   */
  public void render(Geometry geom, RenderManager rm) {
    autoSelectTechnique(rm);

    Renderer r = rm.getRenderer();

    TechniqueDef techDef = technique.getDef();

    if (techDef.getLightMode() == LightMode.MultiPass && geom.getWorldLightList().size() == 0) {
      return;
    }

    if (rm.getForcedRenderState() != null) {
      r.applyRenderState(rm.getForcedRenderState());
    } else {
      if (techDef.getRenderState() != null) {
        r.applyRenderState(
            techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
      } else {
        r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
      }
    }

    // update camera and world matrices
    // NOTE: setWorldTransform should have been called already
    if (techDef.isUsingShaders()) {
      // reset unchanged uniform flag
      clearUniformsSetByCurrent(technique.getShader());
      rm.updateUniformBindings(technique.getWorldBindUniforms());
    }

    // setup textures and uniforms
    for (int i = 0; i < paramValues.size(); i++) {
      MatParam param = paramValues.getValue(i);
      param.apply(r, technique);
    }

    Shader shader = technique.getShader();

    // send lighting information, if needed
    switch (techDef.getLightMode()) {
      case Disable:
        r.setLighting(null);
        break;
      case SinglePass:
        updateLightListUniforms(shader, geom, 4);
        break;
      case FixedPipeline:
        r.setLighting(geom.getWorldLightList());
        break;
      case MultiPass:
        // NOTE: Special case!
        resetUniformsNotSetByCurrent(shader);
        renderMultipassLighting(shader, geom, rm);
        // very important, notice the return statement!
        return;
    }

    // upload and bind shader
    if (techDef.isUsingShaders()) {
      // any unset uniforms will be set to 0
      resetUniformsNotSetByCurrent(shader);
      r.setShader(shader);
    }

    r.renderMesh(geom.getMesh(), geom.getLodLevel(), 1);
  }