Specular and normal maps

I'm an artist trying to import some models into JME, i've noticed in the features list that there's bump mapping, but im also wondering if theres also specular maps and maybe normal maps, and if so, is there a preffered format for exporting? (md5, obj, etc)

Sorry if this is a noobish question or has been stated, i've looked around the site and founf nothing

Have a Look at TestNormalMap.java



I Think the best import waay is with Collada, there you have an ExtraPluginManager, and in this you can put the normal and specular map in your object. But I think we need a good Tutorial for this ExtraPlugin Stuff  :smiley:

Mostly people advise .obj for static models and .md5 for animated models. obj. examples can be found in the jmetest package - good old Maggie. You should also check out this thread - New XML IO based on JME(Im|Ex)porter and compatible Blender exporter.



For animated models Collada might be a possible alternative to MD5 but it has many problems at the moment. Several MD5 importers are in active development though - MD5Importer, MD5 Reader 2 to start you off. Check also this thread - What projects use MD5 format and which importer?



Here are a few threads for browsing:

What model to use?

Animated models - which format and modeller?

Models: Which format for what purpose? No material with md3?



That should get you started. :slight_smile:

thanks for taking the time with a great explanation guy :smiley:

and yep, this should help

Has anyone created an ExtraPlugin in order to get the normalmap working with the COLLADA

importer?

JME support for shaders is a bit limited… I have a test project and there a working normal-spec shader… parallax support can be added with one line in the frag shader. I could put it up if you want.

Yes, thanks.

Hey Guys,

I'm just one more Step ahead and now face extremely disturbing Problems…

I manipulatet the md5Importer a little bit, so that it reads .mtr files (not yet ready, but it may not be the cause of my problems). Now, I am trying  to use the normalmaps shader on my model, but when I set this RenderState of the object, it seems as like every Information about the texturecoordinates get lost. Its a UV-Mapped sword so you can imagine, that I nearly dont see anything with the black background. The ImporterClass already sets one TextureState to the object with the diffusetexture textureID 0. The normalmap and the specularmap is not set to that state but this is only done for testing and because I want the Importer also to Import models without a normal/specularmap.

I tried to follow the TestNormalmap- example as close as possible and tried what happens when i comment the

ts.setTexture(specMap, 2);

ts.setTexture(normalMap, 1); out and there it was no problem, the sphere is just shown with a diffusemap without effect.

Why do my Coordinates go wild with the shader? I hope someone will be able to help me, and sorry for my bad english…

Following I give you some pieces of the code so maybe youll be able to resolve my problem… :slight_smile:


package sandboxtest;


import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import com.jme.app.AbstractGame;
import com.jme.app.SimpleGame;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;
import com.jme.util.resource.MultiFormatResourceLocator;
import com.jme.util.resource.ResourceLocatorTool;
import com.model.md5.ModelNode;
import com.model.md5.importer.MD5Importer;

public class sandbox_arena extends SimpleGame{
   
   
    private GLSLShaderObjectsState so ;
    private String currentShaderStr = "sandboxtest/normalmap";
   
   public static void main(String[] args){
      sandbox_arena app = new sandbox_arena();
      app.setDialogBehaviour(AbstractGame.ALWAYS_SHOW_PROPS_DIALOG);
      app.start();
   }

   protected void simpleInitGame() {
      
      arenaladen();
      
            
      }
   public Texture getTexture(String name){
        Texture result = TextureManager.loadTexture(sandbox_arena.class.getClassLoader().getResource(name),Texture.MM_LINEAR, Texture.FM_LINEAR);
        result.setScale(new Vector3f(4f,4f,1));
        result.setWrap(Texture.WM_WRAP_S_WRAP_T);
           
        return result;
   }
   
   private void arenaladen(){
      
      
            overrideTextureKey();
            so = DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();
            Node soldat = loadModel("sandboxtest/arenaaa.md5mesh");
            Node schwert = loadModel("sandboxtest/3DSMAX/schwert.md5mesh");
            schwert.setRenderState(so);
            //soldat.setRenderState(so);
            schwert.updateRenderState();
            soldat.updateRenderState();
            rootNode.updateGeometricState(0, true);
            rootNode.attachChild(soldat);
            rootNode.attachChild(schwert);
            reloadShader();
            
      
   }
   
   
   protected ModelNode loadModel(String hier) {
      URL md5mesh = sandbox_arena.class.getClassLoader().getResource(hier);
      try {
         MD5Importer.getInstance().loadMesh(md5mesh, "ModelNode");
      } catch (IOException e) {
         e.printStackTrace();
      }
      ModelNode body = MD5Importer.getInstance().getModelNode();
      MD5Importer.getInstance().cleanup();
      body.flagUpdate();
      return body;
   }
   
   protected void overrideTextureKey() {
      try {
         MultiFormatResourceLocator locator = new MultiFormatResourceLocator(sandbox_arena.class.getClassLoader().getResource("sandboxtest/"),
               new String[]{".mtr",".tga", ".bmp", ".png", ".jpg", ".texture", ".jme"});
         ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_TEXTURE, locator);
      } catch (URISyntaxException e) {
         e.printStackTrace();
      }
   }
   
    public void reloadShader() {
           GLSLShaderObjectsState testShader = DisplaySystem.getDisplaySystem()
                   .getRenderer().createGLSLShaderObjectsState();
           try {
               testShader.load(sandbox_arena.class.getClassLoader()
                       .getResource(currentShaderStr + ".vert"),
                       sandbox_arena.class.getClassLoader().getResource(
                               currentShaderStr + ".frag"));
               testShader.apply();
               DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
           } catch (JmeException e) {
              System.out.println("Failed to reload shader");
               return;
           }

           so.load(sandbox_arena.class.getClassLoader().getResource(
                   currentShaderStr + ".vert"), sandbox_arena.class
                   .getClassLoader().getResource(currentShaderStr + ".frag"));

           so.setUniform("baseMap", 0);
           so.setUniform("normalMap", 1);
           so.setUniform("specularMap", 2);

           System.out.println("Shader reloaded...");
       }
   
}


my manipulation @ Mesh.java of the MD5Importer:

private void processTexture() {
      MD5Importer instance = MD5Importer.getInstance();
      FloatBuffer textureBuffer = BufferUtils.createVector2Buffer(this.vertices.length);
      float maxU = 1; float maxV = 1; float minU = 0; float minV = 0;
      for(int i = 0; i < this.vertices.length; i++) {
         BufferUtils.setInBuffer(this.vertices[i].getTextureCoords(), textureBuffer, i);
         if(this.vertices[i].getTextureCoords().x > maxU) maxU = this.vertices[i].getTextureCoords().x;
         else if(this.vertices[i].getTextureCoords().x < minU) minU = this.vertices[i].getTextureCoords().x;
         if(this.vertices[i].getTextureCoords().y > maxV) maxV = this.vertices[i].getTextureCoords().y;
         else if(this.vertices[i].getTextureCoords().y < minV) minV = this.vertices[i].getTextureCoords().y;
      }
      this.triangleBatch.setTextureBuffer(textureBuffer, 0);
      URL url = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, this.texture);
      Texture basemap = TextureManager.loadTexture(url,instance.getMMFilter(),instance.getFMFilter(),instance.getAnisotropic(),true);
      URL urln = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, this.Normtexture);
      Texture normalMap = null;
      if (urln != null) normalMap= TextureManager.loadTexture(urln,instance.getMMFilter(),instance.getFMFilter(),instance.getAnisotropic(),true);
      URL urls = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, this.Spectexture);
      Texture specMap  = null;
      if (urls != null)specMap= TextureManager.loadTexture(urls,instance.getMMFilter(),instance.getFMFilter(),instance.getAnisotropic(),true);
      if(basemap != null) {
         if(maxU > 1 || minU < 0) {
            if(maxV > 1 || minV < 0) basemap.setWrap(Texture.WM_WRAP_S_WRAP_T);
            else {
               if(basemap.getWrap() != Texture.WM_WRAP_S_WRAP_T) {
                  if(basemap.getWrap() == Texture.WM_CLAMP_S_WRAP_T) basemap.setWrap(Texture.WM_WRAP_S_WRAP_T);
                  else basemap.setWrap(Texture.WM_WRAP_S_CLAMP_T);
               }
            }
         } else if(maxV > 1 || minV < 0) {
            if(basemap.getWrap() != Texture.WM_WRAP_S_WRAP_T) {
               if(basemap.getWrap() == Texture.WM_WRAP_S_CLAMP_T) basemap.setWrap(Texture.WM_WRAP_S_WRAP_T);
               else basemap.setWrap(Texture.WM_CLAMP_S_WRAP_T);
            }
         }
      }
      //Normalmaps werden geladen!
      if(urln != null) {
         if(maxU > 1 || minU < 0) {
            if(maxV > 1 || minV < 0) normalMap.setWrap(Texture.WM_WRAP_S_WRAP_T);
            else {
               if(normalMap.getWrap() != Texture.WM_WRAP_S_WRAP_T) {
                  if(normalMap.getWrap() == Texture.WM_CLAMP_S_WRAP_T) normalMap.setWrap(Texture.WM_WRAP_S_WRAP_T);
                  else normalMap.setWrap(Texture.WM_WRAP_S_CLAMP_T);
               }
            }
         } else if(maxV > 1 || minV < 0) {
            if(normalMap.getWrap() != Texture.WM_WRAP_S_WRAP_T) {
               if(normalMap.getWrap() == Texture.WM_WRAP_S_CLAMP_T)normalMap.setWrap(Texture.WM_WRAP_S_WRAP_T);
               else normalMap.setWrap(Texture.WM_CLAMP_S_WRAP_T);
            }
         }
      }
      //Specmaps werden geladen
      if(urls != null) {
         if(maxU > 1 || minU < 0) {
            if(maxV > 1 || minV < 0) specMap.setWrap(Texture.WM_WRAP_S_WRAP_T);
            else {
               if(specMap.getWrap() != Texture.WM_WRAP_S_WRAP_T) {
                  if(specMap.getWrap() == Texture.WM_CLAMP_S_WRAP_T) specMap.setWrap(Texture.WM_WRAP_S_WRAP_T);
                  else specMap.setWrap(Texture.WM_WRAP_S_CLAMP_T);
               }
            }
         } else if(maxV > 1 || minV < 0) {
            if(specMap.getWrap() != Texture.WM_WRAP_S_WRAP_T) {
               if(specMap.getWrap() == Texture.WM_WRAP_S_CLAMP_T)specMap.setWrap(Texture.WM_WRAP_S_WRAP_T);
               else specMap.setWrap(Texture.WM_CLAMP_S_WRAP_T);
            }
         }
      }
      this.triangleBatch.copyTextureCoordinates(0, 0, 1);
      this.triangleBatch.copyTextureCoordinates(0, 2, 1);
      TextureState state = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
      state.setTexture(basemap,0);
      if (urln != null)state.setTexture(normalMap, 2);
      if (urls != null)  state.setTexture(specMap ,3);
      this.triangleBatch.setRenderState(state);
      
   }

bump

sry …  :expressionless:

Does this:


      this.triangleBatch.copyTextureCoordinates(0, 0, 1);
      this.triangleBatch.copyTextureCoordinates(0, 2, 1);
      TextureState state = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
      state.setTexture(basemap,0);
      if (urln != null)state.setTexture(normalMap, 2);
      if (urls != null)  state.setTexture(specMap ,3);
      this.triangleBatch.setRenderState(state);



Need to be this?


      this.triangleBatch.copyTextureCoordinates(0, 1, 1);
      this.triangleBatch.copyTextureCoordinates(0, 2, 1);
      TextureState state = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
      state.setTexture(basemap,0);
      if (urln != null)state.setTexture(normalMap, 1);
      if (urls != null)  state.setTexture(specMap ,2);
      this.triangleBatch.setRenderState(state);



And can you use the MD5Importer w/ jME 2.0? (and does it make difference...)

Yea, your right, this line had to be fixed, but it did not change the appearence of the loaded model :- / . We're using jME 1.0… May that cause any problems?

jME 1.0 is pretty much frozen anymore (no bug fixes, no new features), might be a wise decision to move to jME 2.0 :slight_smile: