Material affecting the whole scene

I try to give a textured model (.obj) a MaterialState.

Once I added this:

ms.setMaterialFace(MaterialState.MF_BACK);

,

the desidered taint appeared but affected everything …









A portion of the code:

(bit messy cause I “played” around to get a result)


tankMesh.clearRenderState(RenderState.RS_MATERIAL);
         
         MaterialState ms = DisplaySystem.getDisplaySystem().getRenderer().createMaterialState();
         ms.setAmbient(new ColorRGBA(0.5f,0.5f,0.5f,0.5f));
         ms.setEmissive(new ColorRGBA(0,0.75f,0,0.5f));
         ms.setDiffuse(new ColorRGBA(0,1,0,0.5f));
         ms.setSpecular(new ColorRGBA(1,0,0,0.5f));
         ms.setShininess(100);
         ms.setMaterialFace(MaterialState.MF_BACK);
         ms.setEnabled(true);
         tankMesh.setRenderState(ms);
         tankMesh.updateRenderState();


do also a rootNode.updateRenderstate() after changing RenderStates maybe that fixes the problem.

could it be that ur scene graph is organized that way?



do u attach ur ground under the tankMesh?

@ Core-Dump: I tryed to put this update everywhere I thought it could make sens but I got no single change  :expressionless:



@Neakor:  yes the first time I saw this result I said to myself it was a mistake in my scenegraph but I triple checked and everything is right  :// (my scene graph is really simple: under the rootnode i have attached the green field and the occluder node. Under the occluder node i attached the model node  and of course i have the fps node but separated).

To be sure I removed all previous materialstate also.



I'm gonna test more to figure it out  :stuck_out_tongue:

I just came back to this problem.

I tested something: I applied this MaterialState to the rootNode and, as expected, the whole scene is also tinted.

One thing I wonder is: when I apply a LightState to a rootNode, all his children are also affected ?



Anyway seems I have to give up with materialStates. Is there another way to tint a Node ?



EDIT: damn, completely crazy,  I tested further and I come up with this:

let A, B and C 3 Nodes.

A has a lightState with a light turned on.

if I attach B and C to A and I apply the materialState to B, then C is also affected  :? duh !



I'm sure of my scenegrapg cause I applied a translation to B and only B got affected by it.

Only the kids inherit the RenderStates of the parents.

Double check that you're not mixing up references and stuff.



Can you break down the issue into a small demo app?

I was writing a simple demo app to show you and by curiosity I replaced my model by a box and…The box is not affected  :?

How is that possible ?

I'm gonna attach the model (3 TriMeshes) with the source so and post it here in a few minutes.

Shrank the maximum I could and the problem is still here on my side.



Normally directly a copy/paste of this should be working:


import com.jme.bounding.BoundingBox;
import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.SceneElement;
import com.jme.scene.Text;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.LightState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jmex.game.state.GameState;


public class TsGameState extends GameState
{
   private Renderer renderer;
   
   protected StringBuffer updateBuffer = new StringBuffer( 30 );
   protected StringBuffer tempBuffer = new StringBuffer();
   protected Timer timer;
   protected long targetedFps;
   protected Camera cam;
    protected Node fpsNode;
    protected Text fpsText;
    protected Node gameInfoNode;
    protected Text gameInfoText;
    protected InputHandler input;
    public static String fontLocation = Text.DEFAULT_FONT;
   
    private static LightState mainLightState = null;
   private Node rootNode = null;
   private Node occluders = null;
   private TsItemLoader tsItemLoader = null;
   
   
   public TsGameState(String _name, long _targetedFps)
   {   
      setName(_name);
      
      timer = Timer.getTimer();
      
      targetedFps = _targetedFps;
      
      renderer = DisplaySystem.getDisplaySystem().getRenderer();
      
      cam = renderer.getCamera();
      
      gameInfoText = Text.createDefaultTextLabel( "InfoText label" );
      gameInfoText.setCullMode( SceneElement.CULL_NEVER );
      gameInfoText.setTextureCombineMode( TextureState.REPLACE );
      
      gameInfoNode = new Node( "gameInfo node" );
      gameInfoNode.setRenderState( gameInfoText.getRenderState( RenderState.RS_ALPHA ) );
      gameInfoNode.setRenderState( gameInfoText.getRenderState( RenderState.RS_TEXTURE ) );
      gameInfoNode.attachChild( gameInfoText );
      gameInfoNode.setCullMode( SceneElement.CULL_NEVER );
      gameInfoNode.setLocalTranslation(0,DisplaySystem.getDisplaySystem().getHeight()-20,0);
      
      fpsText = Text.createDefaultTextLabel( "FPS label" );
      fpsText.setCullMode( SceneElement.CULL_NEVER );
      fpsText.setTextureCombineMode( TextureState.REPLACE );

        fpsNode = new Node( "FPS node" );
        fpsNode.setRenderState( fpsText.getRenderState( RenderState.RS_ALPHA ) );
        fpsNode.setRenderState( fpsText.getRenderState( RenderState.RS_TEXTURE ) );
        fpsNode.attachChild( fpsText );
        fpsNode.setCullMode( SceneElement.CULL_NEVER );
       
        rootNode = new Node("rootNode");
        ZBufferState buf = renderer.createZBufferState();
        buf.setEnabled( true );
        buf.setFunction( ZBufferState.CF_LEQUAL );
        rootNode.setRenderState( buf );
      
        occluders = new Node();
        rootNode.attachChild(occluders);
       
        FirstPersonHandler firstPersonHandler = new FirstPersonHandler( cam, 50,1 );
        input = firstPersonHandler;

      loadWorld();

        timer.reset();

        rootNode.updateGeometricState( 0.0f, true );
        rootNode.updateRenderState();
        fpsNode.updateGeometricState( 0.0f, true );
        fpsNode.updateRenderState();
        gameInfoNode.updateGeometricState( 0.0f, true );
        gameInfoNode.updateRenderState();

        timer.reset();
       
        renderer.enableStatistics( true );
   }

   public void loadWorld()
   {
      Node groundNode = new Node();      
            Quaternion roll90 = new Quaternion();
            roll90.fromAngleAxis( FastMath.PI/2 , new Vector3f(1,0,0) );
      
            Quad quad = new Quad("",32,32);
                quad.setLocalRotation( roll90 );
            quad.setModelBound(new BoundingBox());
            quad.updateModelBound();
            groundNode.attachChild(quad);
            
      rootNode.attachChild(groundNode);
      
      DirectionalLight dr1 = new DirectionalLight();
        dr1.setDiffuse(ColorRGBA.white);
        dr1.setAmbient(new ColorRGBA(0.2f, 0.2f, 0.2f, 0.4f));
        dr1.setDirection(new Vector3f(-1f, -1f, 1f).normalizeLocal());
        dr1.setShadowCaster(true);
        dr1.setEnabled(true);
      
        mainLightState = renderer.createLightState();
        mainLightState.attach(dr1);
        mainLightState.setEnabled(true);
        mainLightState.setGlobalAmbient(new ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f));
        mainLightState.setSeparateSpecular(true);
        rootNode.setRenderState(mainLightState);
      
      
      MaterialState ms = DisplaySystem.getDisplaySystem().getRenderer().createMaterialState();
      ms.setAmbient(new ColorRGBA(0.8f,0.1f,0.1f,0.5f));
      ms.setEmissive(new ColorRGBA(1,0.0f,0,0.5f));
      ms.setDiffuse(new ColorRGBA(1,0,0,0.5f));
      ms.setSpecular(new ColorRGBA(1,0,0,0.5f));
      ms.setShininess(50);
      ms.setEnabled(true);
      ms.setMaterialFace(MaterialState.MF_FRONT_AND_BACK);
      groundNode.setRenderState(ms);
      groundNode.updateRenderState();
      
      
      Box box =new Box("my box",new Vector3f(1,1,1),new Vector3f(2,2,2));
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      
      tsItemLoader = new TsItemLoader();
      Node tankNode = tsItemLoader.loadTankItem();
      tankNode.setLocalTranslation(10,0,10);
      occluders.attachChild(tankNode);
   }
   
   
//******DO NOT TOUCH BELOW*********************************   
   
   public Node getOccludersNode()
   {
      return occluders;
   }
   
   public Node getRootNode()
   {
      return rootNode;
   }
   
   public Node getFpsNode()
   {
      return fpsNode;
   }
//   
   @Override
    public void render(float tpf)
   {
        renderer.draw(rootNode);
        renderer.draw(fpsNode);
        renderer.draw(gameInfoNode);
    }
   
    @Override
    public void update(float tpf)
    {
       timer.update();
        tpf = timer.getTimePerFrame();
       
        updateBuffer.setLength( 0 );
        updateBuffer.append( "FPS: " ).append( (int) timer.getFrameRate() ).append(" - " );
        updateBuffer.append( renderer.getStatistics( tempBuffer ) );
        fpsText.print( updateBuffer );
       
         fpsNode.updateGeometricState(tpf, true);
         gameInfoNode.updateGeometricState(tpf, true);
         rootNode.updateGeometricState(tpf, true);
         
         input.update(tpf);
    }
   
    @Override
    public void cleanup()
    {
       TextureManager.doTextureCleanup();
        if (DisplaySystem.getDisplaySystem() != null && renderer != null) renderer.cleanup();
        KeyInput.destroyIfInitalized();
        MouseInput.destroyIfInitalized();
    }
}




import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import com.jme.bounding.BoundingSphere;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.ObjToJme;


public class TsItemLoader
{
   public Node loadTankItem()
   {
      ObjToJme converter = new ObjToJme();
      try
        {
         URL bodyObjFile = TsItemLoader.class.getClassLoader().getResource("resources/models/tank/body.obj");
         converter.setProperty("mtllib", bodyObjFile);
            converter.setProperty("texdir",bodyObjFile);
         ByteArrayOutputStream BO = new ByteArrayOutputStream();
         System.out.println("Starting to convert .obj to .jme");
         converter.convert(bodyObjFile.openStream(),BO);
         TriMesh bodyMesh = (TriMesh)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
         
         bodyMesh.setLocalScale(0.6f);
         Quaternion roll180 = new Quaternion();
         roll180.fromAngleAxis( FastMath.PI , new Vector3f(0,1,0) );
         bodyMesh.setLocalRotation(roll180);
         bodyMesh.setModelBound(new BoundingSphere());
         bodyMesh.updateModelBound();
         
         Node tankNode = new Node();
         tankNode.attachChildAt(bodyMesh,0);
         tankNode.setLocalScale(0.8f);
         
         return tankNode;
      }
      catch (IOException e)
        {
         e.printStackTrace();
         return null;
      }
   }
}




Main Class:

import com.jme.input.MouseInput;
import com.jme.system.DisplaySystem;
import com.jmex.game.StandardGame;
import com.jmex.game.state.GameStateManager;


public class TsStandardGame
{
   public static void main(String[] args)
   {
      @SuppressWarnings("unused")
      TsStandardGame sg = new TsStandardGame();
   }
   
   public TsStandardGame()
   {
      
      StandardGame game = new StandardGame("TankSport");
      
      game.getSettings().setVerticalSync(false);
      DisplaySystem.getDisplaySystem().setMinSamples(4);
      game.getSettings().setStencilBits(4);
        game.getSettings().setDepth( 16 );
        game.getSettings().setAlphaBits( 0 );
        game.getSettings().setSamples( 4 );
        game.getSettings().setFramerate(-1);
       
      game.start();

      MouseInput.get().setCursorVisible(true);
      
      TsGameState tsGameState = new TsGameState("tsGameState",60);
      GameStateManager.getInstance().attachChild(tsGameState);
      tsGameState.setActive(true);
   }
}



And the simplified model (missing the turret but doesn't matter  ;)):

http://www.mediafire.com/?f9wen0aydgr

Thanks a lot for your help Core-Dump !

do you get the desired behavior  if you comment out ms.setMaterialFace(MaterialState.MF_FRONT_AND_BACK); ?

When this line is ignored the whole scene turns back to the normality (with no appearent tint ).

Since this issue  occurs with a blender .obj model but not with a 3D primitive like a box, I will steer my tests on loading different formats like  3DS models or else to see what happens.

But it would be very surprising the fact that it's a .obj mess up everything !

i don't know why this fixes it, i tried to reproduce the problem with a simple scene but its working correctly there :slight_smile:

:// even with the model I provided ?

hmm no your model messes up the states somehow :slight_smile:



maybe the model's normal are flipped

Damn Blender if so  :stuck_out_tongue: . If I reverse the normals I guess I won't see  any texture :-s.

I found and downloading an other model .obj made under another software. I'm gonna test it.

I'll also try to export my model from blender but with another format (If you can advice me a good one  :P).


I tried with a model from jme tests (maggie.obj), it works.



I definitely have to change for another 3D modeler but I only know Blender as a free one. Any advice ?



edit: found Wings3D, let's get cracking !

Did you check the wiki for exporting hints?

http://www.jmonkeyengine.com/wiki/doku.php?do=search&id=blender



I have no experience with exporting models but I'm sure it works with the correct settings.

I followed the exact procedure and still the same result.



I just tried Testmd3JmeWrite and the problem is still there  :? :? :?



If you wanna try, just change the previous class given some posts before called "TsItemLoader" into this:


public class TsItemLoader
{
   public Spatial loadTankItem()
   {
      Md3ToJme converter=new Md3ToJme();
        URL model=null;
        model=TestMd3JmeWrite.class.getClassLoader().getResource("jmetest/data/model/nordhorse.md3");
        URL tex=TestMd3JmeWrite.class.getClassLoader().getResource("jmetest/data/model/nordhorse_color.jpg");
        ByteArrayOutputStream BO=new ByteArrayOutputStream();
        try {
            converter.convert(model.openStream(),BO);
            long time=System.currentTimeMillis();
            Spatial r=(Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            r.setModelBound(new BoundingBox());
            r.updateModelBound();
            TextureState ts= DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
            ts.setTexture(TextureManager.loadTexture(tex,Texture.MM_LINEAR,Texture.FM_LINEAR));
            ts.setEnabled(true);
            r.setRenderState(ts);
            return r;
        } catch (IOException e) {
           return null;
        }
   }
}



I do think my code is not good and possibly obvious but I really can't see it.

i dunno,

this is the simplest test i could come up with, maybe someone else knows why this happens:


public class SimpleMaterialFace extends SimpleGame {

    public SimpleMaterialFace() {
    }

    @Override
    protected void simpleInitGame() {
       
        Md3ToJme converter=new Md3ToJme();
        Node modelNode = null;
        try
        {
            URL model=TestMd3JmeWrite.class.getClassLoader().getResource("jmetest/data/model/nordhorse.md3");
            URL tex=TestMd3JmeWrite.class.getClassLoader().getResource("jmetest/data/model/nordhorse_color.jpg");
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            System.out.println("Starting to convert .obj to .jme");
            converter.convert(model.openStream(),BO);
            Spatial bodyMesh = (Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            TextureState ts= DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
            ts.setTexture(TextureManager.loadTexture(tex,Texture.MM_LINEAR,Texture.FM_LINEAR));
            ts.setEnabled(true);
            bodyMesh.setRenderState(ts);
            bodyMesh.setLocalScale(0.6f);
            Quaternion roll180 = new Quaternion();
            roll180.fromAngleAxis( FastMath.PI , new Vector3f(0,1,0) );
            bodyMesh.setLocalRotation(roll180);
            bodyMesh.setModelBound(new BoundingSphere());
            bodyMesh.updateModelBound();
           
            modelNode = new Node("model node");
            modelNode.attachChild(bodyMesh);
            modelNode.setLocalScale(0.8f);
           
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
       
        Box b1 = new Box("box 1", new Vector3f(5, 0, 0), 1, 1, 1);
        b1.setModelBound(new BoundingBox());
        b1.updateModelBound();
        modelNode.attachChild(b1);
       
        MaterialState ms = display.getRenderer().createMaterialState();
        ms.setAmbient(new ColorRGBA(0.8f,0.1f,0.1f,0.5f));
        ms.setEmissive(new ColorRGBA(1,0.0f,0,0.5f));
        ms.setDiffuse(new ColorRGBA(1,0,0,0.5f));
        ms.setSpecular(new ColorRGBA(1,0,0,0.5f));
        ms.setShininess(50);
        ms.setEnabled(true);
        ms.setMaterialFace(MaterialState.MF_FRONT_AND_BACK);
       
        Box b2 = new Box("box 2", new Vector3f(-5, 0, 0), 1, 1, 1);
        b2.setModelBound(new BoundingBox());
        b2.updateModelBound();
        Node n2 = new Node("node 2");
        b2.setRenderState(ms);
        n2.attachChild(b2);
       
        rootNode.attachChild(modelNode);
        rootNode.attachChild(n2);
       
        SceneGraphDump.dump(rootNode);
    }

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

}



with MF_FRONT_AND_BACK the Horsey is red too

Thx again for your help.



So, excepting when it's double-sided, it works. I have to revise some things in my standardgame I guess  :oops:



edit: when you write this instead, the horse doesn't get affected by the material:


modelNode.setRenderState(ms);

  ...But with MF_FRONT_AND_BACK  in that case everything is ok hahaha what a mess !!

Finally I obtained what I needed (but…)





http://img1.imagilive.com/affiche/0408/success.jpg.htm



…But I did it by loading a textured md3 model I made with Blender. Therefore the problem is possibly due to the .obj format when imported from Blender.

Also what is cool, seems the md3 exporter automatically flips the normals !



Thanks a lot Core-Dump ! Comme disent les suisses: “c’est porno !”  8)