.obj model displays with no Texture

Hi,



I have a class that creates a node and returns from a .obj, this bit works but it won't render the texture from its .mtl file. I've tried the "updateRenderState" method, this doesn't help. If the "converter.setProperty("mtllib"…);" line is removed it fails to run, but included and supplied with a wrong URL parameter it runs without complaint.



I'm probably making another newbie mistake, but I can't figure out where.

You need to use the ResourceLocatorTool and SimpleResourceLocator.  See the test code in cvs or search the forum for more.

I'd also point out that the Obj loader seems to ignore the command "map_Kd" (exported by blender) which is supposed to apply the texture to the material given a filename.

hmm i can't confirm that, my spaceship which is a .obj does load the textures which are defined with map_Kd.

Right, see in the ObjToJme class (around line 325 - well, in my local jME 2.0 copy):


        } else if ("map_Kd".equals(parts[0]) || "map_Ka".equals(parts[0])) {


That's odd  :?

Might be a problem in MonkeyWorld…

I still can't get the texture to load. This is the code I'm using, it's basically the same code as HelloLOD but it returns the Node instead of attaching it to the rootNode:

private Node LoadObject(String model_name){
   // Point to a URL of the model
        URL model=ObjectNode.class.getClassLoader().getResource("jmetest/data/model/maggie.obj");
        URL texture=ObjectNode.class.getClassLoader().getResource("jmetest/data/model/maggie.mtl");

   try{
      SimpleResourceLocator  srl = new SimpleResourceLocator(texture);
      ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_TEXTURE,srl);
   }
   catch(Exception e){}

        // Create something to convert .obj format to .jme
        FormatConverter converter=new ObjToJme();

        // Point the converter to where it will find the .mtl file
        converter.setProperty("mtllib",texture);
        //converter.setProperty("texdir",texture);

        // This byte array will hold my .jme file
        ByteArrayOutputStream BO=new ByteArrayOutputStream();
        Spatial modelObject = null;
        try{           
            // Use the format converter to convert .obj to .jme
            converter.convert(model.openStream(), BO);

            // Load the binary .jme format into a scene graph
            modelObject=(Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
        }
        catch(Exception e){ // Just in case anything happens          
            System.exit(0);
        }
       
        // Create a clod duplicate of meshParent.
        Node clodNode=getClodNodeFromParent((Node)modelObject);

        return(clodNode);
   }


At first I thought it might have been an problem with the model I was using, but switching to the maggie model has the same effect.

And what happens when you attach the returned node from LoadObject() to the rootNode (and update renderstates on the root node after)?

Do you see the model without textures, or no model at all?



Maybe post the whole source, so its easier to see what you are doing.

The full source is somewhat lengthy but here are the two functions that result in the ObjectNode being created:

protected void initGame(){
       scene = new Node("Scene graph node");
              
       /** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
        ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.CF_LEQUAL);
        scene.setRenderState(buf);
       
        //Time for a little optimization. We don't need to render back face triangles, so lets
        //not. This will give us a performance boost for very little effort.
        CullState cs = display.getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        scene.setRenderState(cs);
       
        //Add terrain to the scene
        buildArea();
        //Build the surrounding star field
       build_starField();
       //Build the player
        buildPlayer();
        positionPlayer();
        //Build the player's HUD
        buildHUD();
        //Build the camera
        buildCamera();
        positionCamera();
        //Build the player input
        buildInput();

       // update the scene graph for rendering
        scene.updateGeometricState(0.0f, true);
    }



private void buildPlayer(){
        player = new Node("Player Node");
        player.setModelBound(new BoundingSphere());
        scene.attachChild(player);
        player.updateWorldBound();

        ObjectNode playerVessel = new ObjectNode("player_vessel","string");
        playerVessel.setLocalScale(.1f);
        player.attachChild(playerVessel.ReturnNode());
        player.updateWorldBound();
        scene.updateRenderState();
    }


The model loads but with no textures.

It appears that the .mtl file is being read correctly and the textures being added when the Node is created, however it just isn't being applied to the model when it is displayed. I have tried moving the code that creates the model node into the main class, but that has the same result. Any ideas?

Okay, here is the offending code in all its glory (well not all its glory, but enough to compile to show the problem).



test2.java

import com.jme.app.BaseGame;
import com.jme.bounding.BoundingSphere;
import com.jme.renderer.Camera;
import com.jme.scene.Node;
import com.jme.scene.CameraNode;
import com.jme.scene.state.CullState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.math.Vector3f;
import packages.ObjectNode;

public class test2 extends BaseGame{   
    final boolean useBillboardNodes = true;
   
    private Node scene = null;

    private Node player;

    private Camera cam;
    private CameraNode camNode;

    private int width, height, depth, freq;
    private boolean fullscreen;

    public static void main(String[] args){
       test2 app=new test2();
        app.start();       
    }

    protected void update(float interpolation){
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
            finished = true;
        }

        scene.updateGeometricState(interpolation, true);
    }


    protected void render(float interpolation) {
        display.getRenderer().clearBuffers();
        display.getRenderer().draw(scene);      
    }

    protected void initSystem(){
        width = properties.getWidth();
        height = properties.getHeight();
        depth = properties.getDepth();
        freq = properties.getFreq();
        fullscreen = properties.getFullscreen();
       
        try {
            display = DisplaySystem.getDisplaySystem(properties.getRenderer());
            display.createWindow(width, height, depth, freq, fullscreen);

            cam = display.getRenderer().createCamera(width, height);
        }
        catch (JmeException e) {
           e.printStackTrace();
            System.exit(1);
        }
       
        display.getRenderer().setCamera(cam);

        KeyBindingManager.getKeyBindingManager().set("exit",KeyInput.KEY_ESCAPE);       
    }

    protected void initGame(){
       scene = new Node("Scene graph node");
       
        ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.CF_LEQUAL);
        scene.setRenderState(buf);
       
        CullState cs = display.getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        scene.setRenderState(cs);

        buildPlayer();
        buildCamera();

        scene.updateModelBound();
        scene.updateWorldBound();
        scene.updateWorldVectors();

        cam.update();
       
        scene.updateGeometricState(0.0f, true);
    }

    private void buildPlayer(){
        player = new Node("Player Node");
        player.setModelBound(new BoundingSphere());
        scene.attachChild(player);
        player.updateWorldBound();

        ObjectNode playerVessel = new ObjectNode("player_vessel","string");
        playerVessel.setLocalScale(.1f);
        player.attachChild(playerVessel.ReturnNode());
        player.updateWorldBound();
        player.updateWorldBound();
        player.updateRenderState();
       
        player.setLocalTranslation(new Vector3f(0,0,0));
        scene.updateRenderState();
   
       player.setLocalTranslation(0f,0f,0f);       
    }

    private void buildCamera(){
      cam.setFrustum(1.0f, 1000.0f, -0.55f, 0.55f, 0.4125f, -0.4125f);
      cam.update();
      
      camNode = new CameraNode("Camera Node", cam);
      camNode.updateWorldData(0);
      
      player.attachChild(camNode);      
   
        Vector3f tempPos = new Vector3f();
        tempPos.x=0f;
        tempPos.y=80f;
        tempPos.z=-80f;
               
        camNode.setLocalTranslation(tempPos);
       
        camNode.lookAt(new Vector3f(0f,0f,0f), new Vector3f(0,1,0));
    }

    protected void reinit() {
        display.recreateWindow(width, height, depth, freq, fullscreen);
    }

    protected void quit(){
        super.quit();
        System.exit(0);
    }
   
    /* (non-Javadoc)
    * @see com.jme.app.BaseGame#cleanup()
    */
   @Override
   protected void cleanup(){
      // Clean up the mouse
      MouseInput.get().removeListeners();
      MouseInput.destroyIfInitalized();
      // Clean up the keyboard
      KeyInput.destroyIfInitalized();
   }   
}



ObjectNode.java

package packages;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.net.URISyntaxException;
import java.net.URL;
import com.jme.bounding.BoundingSphere;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.lod.AreaClodMesh;
import com.jme.scene.state.RenderState;
import com.jme.util.export.Savable;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.FormatConverter;
import com.jmex.model.converters.ObjToJme;
import com.jme.util.resource.ResourceLocatorTool;
import com.jme.util.resource.SimpleResourceLocator;

public class ObjectNode extends Node implements Serializable, Savable{
   private static final long serialVersionUID = 3L;
   
   Node modelNode;
   
   public ObjectNode(){ }

   public ObjectNode(String name, String model_name){
      super(name);
      this.modelNode=LoadObject(model_name);
      this.modelNode.updateModelBound();
   }

   public Node ReturnNode(){
      return this.modelNode;
   }

   public void setLocalTranslation(Vector3f newTranslation){
      this.modelNode.setLocalTranslation(newTranslation);
   }
   
   public void setLocalScale(float newScale){
      this.modelNode.setLocalScale(newScale);
   }

   public void updateRenderState(){
      this.modelNode.updateRenderState();
   }

   private Node LoadObject(String model_name){
      // Point to a URL of the model
        URL model=ObjectNode.class.getClassLoader().getResource("jmetest/data/model/maggie.obj");
      URL texture=ObjectNode.class.getClassLoader().getResource("jmetest/data/model/maggie.mtl");
      
      try{
         SimpleResourceLocator srl = new SimpleResourceLocator(model);
         ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_TEXTURE,srl);
      }
      catch(URISyntaxException e1){
         System.out.println("URI exception");
         e1.printStackTrace();
       }
      catch(Exception e2){
         System.out.println("Exception");
         e2.printStackTrace();
      }

        // Create something to convert .obj format to .jme
        FormatConverter converter=new ObjToJme();

        // Point the converter to where it will find the .mtl file
        converter.setProperty("mtllib",texture);

        // This byte array will hold my .jme file
        ByteArrayOutputStream BO=new ByteArrayOutputStream();
        Spatial modelObject = null;
        try{           
            // Use the format converter to convert .obj to .jme
            converter.convert(model.openStream(), BO);

            // Load the binary .jme format into a scene graph
            modelObject=(Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));           
        }
        catch(Exception e){ // Just in case anything happens          
            System.exit(0);
        }

        // Create a clod duplicate of meshParent.
        Node clodNode=getClodNodeFromParent((Node)modelObject);

        clodNode.setName("Clod node");

        return(clodNode);
   }

   private Node getClodNodeFromParent(Node meshParent){
        // Create a node to hold my cLOD mesh objects
        Node clodNode=new Node("New clod node");
        // For each mesh
        for(int i=0;i<meshParent.getQuantity();i++){
            final Spatial child = meshParent.getChild(i);

            if(child instanceof Node){
                clodNode.attachChild( getClodNodeFromParent( (Node) child ) );
            }
            else if(child instanceof TriMesh){
                // Create an AreaClodMesh for that mesh.  Let it compute records automatically
                AreaClodMesh acm=new AreaClodMesh("part"+i,(TriMesh) child,null);
                acm.setModelBound(new BoundingSphere());
                acm.updateModelBound();

                // Allow 1/2 of a triangle in every pixel on the screen in the bounds.
                acm.setTrisPerPixel(.5f);

                // Force a move of 2 units before updating the mesh geometry
                acm.setDistanceTolerance(2);

                // Give the clodMesh node the material state that the original had.
                acm.setRenderState(child.getRenderState(RenderState.RS_MATERIAL));

                // Attach clod node.
                clodNode.attachChild(acm);
            }
            else{
            }
        }
        return clodNode;
    }
}



I'm fairly certain it is something to do with the RenderState, but I wouldn't be entirely surprised to find I'm completely wrong there also.

and there shall be light!

add this in your initGame() method :slight_smile:



        PointLight light = new PointLight();
        light.setDiffuse( new ColorRGBA( 0.75f, 0.75f, 0.75f, 0.75f ) );
        light.setAmbient( new ColorRGBA( 0.5f, 0.5f, 0.5f, 1.0f ) );
        light.setLocation( new Vector3f( 100, 100, 100 ) );
        light.setEnabled( true );

        /** Attach the light to a lightState and the lightState to rootNode. */
        LightState lightState = display.getRenderer().createLightState();
        lightState.setEnabled( true );
        lightState.attach( light );
        scene.setRenderState( lightState );

Superb, that works.



Thanks for the quick solution :slight_smile: