How can I use CartoonEdgeFilter for only one spatial in the viewport?

I try to modify the CartoonEdgeFilter → MyEdgeFilter in order to make only one spatial highlight when I click it but I don’t really understand how “CartoonEdgeFilter” work:



I have

  1. myCam which clone() of sceneCam
  2. myScene which contain only my highlight_Spatial



    [java] public void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {

    normalPass = new Pass();

    normalPass.init(renderManager.getRenderer(), w, h, Format.RGBA8, Format.Depth);

    material = new Material(manager, “Common/MatDefs/Post/CartoonEdge.j3md”);



    myCam = new Camera(w, h);

    myView = new ViewPort(“myView”, myCam);

    myView.setClearEnabled(true);

    myView.attachScene(myScene);

    myView.setOutputFrameBuffer(normalPass.getRenderFrameBuffer());



    material.setFloat(“EdgeWidth”, edgeWidth);

    material.setFloat(“EdgeIntensity”, edgeIntensity);

    material.setFloat(“NormalThreshold”, normalThreshold);

    material.setFloat(“DepthThreshold”, depthThreshold);

    material.setFloat(“NormalSensitivity”, normalSensitivity);

    material.setFloat(“DepthSensitivity”, depthSensitivity);

    material.setColor(“EdgeColor”, edgeColor);

    }[/java]

    [java]public void preRender(RenderManager renderManager, ViewPort viewPort) {

    Camera sceneCam = viewPort.getCamera();



    myCam=sceneCam.clone();



    Renderer r = renderManager.getRenderer();

    r.setFrameBuffer(normalPass.getRenderFrameBuffer());

    renderManager.getRenderer().clearBuffers(true, true, true);

    renderManager.setForcedTechnique(“PreNormalPass”);



    //renderManager.renderViewPortQueues(myView, false);

    renderManager.renderViewPort(myView, savedTpf);

    renderManager.setForcedTechnique(null);

    renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());

    //renderManager.setCamera(sceneCam, false);

    }[/java]



    So the problem is I don’t really know when “myView” is rendered ???
atomix said:
So the problem is I don't really know when "myView" is rendered ???

line 12 of the second block

renderManager.renderViewPort(myView, savedTpf);


if the question is "when, in time", it's rendered just before the main viewport render.

Yah… already got it work! :smiley: Thank you Nehon! Wish you a good day!

I am also trying to use the Cartoon Edge filter on only certain spatials in the scene. I’m afraid I don’t understand most of what is posted above.



Do I need to have two completely different scene graphs, with one containing only the spatials I want highlighted?



Do I need to render each scene graph seperately?



If someone has code to do what is needed, could you please post it?

2 Likes

This is hard to understand.

Could Nehon or Atomix give us more detail? I really wander how it works.



Best regards.

@tomi and @jon81 , there is another shader for objects just for your interest.



ToonBlow, have a try it. there is entire JMP project with examples.

http://code.google.com/p/jme-glsl-shaders/

@atomix, could you post your complete codes of your example? I also have the same question that how to highlight one object but not whole scene when click the object.

Is anybody know the details how to do this? I also have the same requirement with host atomix.

@longyg :

Ah, sorry, I missed this post.

Let me see, now I use the the ToonEdge shader in ShaderBlow pack which @mifth metioned above.

Before that solution, I used my own modified version of Edge Filter which take just the Geometry you need, add into a separate Scene node, attach into a PreView … ( You may find this quite difficult to understand if you 're new to JME) .



OK , let me find my code:

Now it will be a little bit complex routine but I will tell you step by step:

  1. You have to download ShaderBlow shader collection http://code.google.com/p/jme-glsl-shaders/
  2. Make a CartoonEdgeFilter ( also in ShaderBlow Test code, copy that)

    [java]



    public class CartoonEdgeProcessor implements SceneProcessor {



    RenderManager rm;

    ViewPort vp;



    public CartoonEdgeProcessor() {

    }



    @Override

    public void initialize(RenderManager rm, ViewPort vp) {

    this.rm = rm;

    this.vp = vp;

    }



    @Override

    public void reshape(ViewPort vp, int w, int h) {

    }



    @Override

    public boolean isInitialized() {

    if (rm != null) {

    return true;

    }

    return false;

    }



    @Override

    public void preFrame(float tpf) {

    }



    @Override

    public void postQueue(RenderQueue rq) {

    rm.setForcedTechnique("CartoonEdge");

    rm.renderViewPortQueues(vp, false);

    rm.setForcedTechnique(null);

    }



    @Override

    public void postFrame(FrameBuffer out) {

    }



    @Override

    public void cleanup() {

    }

    }

    [/java]
  3. You object, apply the Material LightBlow, which also in that Test source… If you object is now in official JME Lightning material, you can convert it to LightBlow. Here is my code:

    MateriaManager.java

    [java]

    public static boolean isLightBlowMat(MaterialDef matDef) {

    return (matDef.getAssetName().endsWith("LightBlow.j3md"));

    }



    public static boolean isLightingMat(MaterialDef matDef) {

    return (matDef.getAssetName().endsWith("Lighting.j3md"));

    }



    public Material convertToLightBlow(Material orgMat) {

    // check if org is Lighting.jmd

    if (isLightingMat(orgMat.getMaterialDef())) {

    // construct the clone Mat



    Material newMat = new Material(assetManager, "MatDefs/LightBlow/LightBlow.j3md");

    Texture diffuseMap = orgMat.getTextureParam("DiffuseMap").getTextureValue();

    newMat.setTexture("DiffuseMap", diffuseMap);

    return newMat;

    } else {

    return orgMat;

    }

    }

    [/java]
  4. A PickingControl attach to that Geometry which help to show the highlight when selected or shut it when deselected:

    [java] void doSelected() {

    // make it highlight

    Material targetMat = targetGeo.getMaterial();

    MaterialDef matDef = targetMat.getMaterialDef();

    //System.out.println(matDef.getAssetName() + matDef.getName());



    if (MaterialManager.isLightingMat(matDef)) {

    targetMat.setBoolean(“Minnaert”, true);

    targetMat.getAdditionalRenderState().setBlendMode(BlendMode.Additive);

    }

    targetGeo.getMesh().setLineWidth(3f);



    savedMat = targetMat.clone();

    Material newMat = worldManager.getMaterialManager().convertToLightBlow(targetMat);

    newMat.setBoolean(“Toon”, true);

    newMat.setFloat(“EdgeSize”, 0.2f);

    newMat.setColor(“EdgesColor”, ColorRGBA.Blue);

    newMat.setBoolean(“Fog_Edges”, true);

    newMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

    newMat.getAdditionalRenderState().setWireframe(true);

    targetGeo.setMaterial(newMat);





    // tell the entity manager that is the current picked entity.

    // rememeber the oldParentNode and the translation

    // attach to characterLookNode

    }



    void doDeselected() {

    // make it normal

    Material targetMat = targetGeo.getMaterial();

    MaterialDef matDef = targetMat.getMaterialDef();

    //System.out.println(matDef.getAssetName() + matDef.getName());

    if (MaterialManager.isLightingMat(matDef)) {

    targetMat.setBoolean(“Minnaert”, false);

    targetMat.getAdditionalRenderState().setBlendMode(BlendMode.Off);

    }

    if (MaterialManager.isLightBlowMat(matDef)) {

    targetMat.setBoolean(“Toon”, false);

    targetMat.setFloat(“EdgeSize”, 0f);

    targetMat.setColor(“EdgesColor”, ColorRGBA.Blue);

    targetMat.setBoolean(“Fog_Edges”, false);

    targetMat.getAdditionalRenderState().setBlendMode(BlendMode.Off);

    targetMat.getAdditionalRenderState().setWireframe(false);

    }

    }

    [/java]



    Any question, don’t hesitate to ask! Have fun with JME!

Many thanks atomix.

I add your codes to my app, but now i have not tested pass them.

One question from your sentence “If you object is now in official JME Lightning material”, you mean the object who want to use LightBlow material must be had lighting material at first?

@longyg:

you mean the object who want to use LightBlow material must be had lighting material at first?



Not really, you can apply LightBlow material directly, but if you are using Lighting material in that Geometry, I also give you a method to convert it to LightBlow so you can use it and just set the Edge value of the Material. cool?



Sorry if my explanation is difficult to understand, :stuck_out_tongue:

I used Unshaded.j3md material, but it can not be converted.

Please look at all my codes:

SelectionManager.java

[java]

package com.longyg.jme3.selection;



import com.jme3.asset.AssetManager;

import com.jme3.material.Material;

import com.jme3.material.MaterialDef;

import com.jme3.material.RenderState.BlendMode;

import com.jme3.math.ColorRGBA;

import com.jme3.scene.Geometry;



public class SelectionManager {

private Geometry targetGeo;

private Material savedMat;



private AssetManager assetManager;



public SelectionManager(Geometry targetGeo, AssetManager assetManager) {

this.targetGeo = targetGeo;

this.assetManager = assetManager;

}



void doSelected() {

// make it highlight

Material targetMat = targetGeo.getMaterial();

MaterialDef matDef = targetMat.getMaterialDef();

//System.out.println(matDef.getAssetName() + matDef.getName());



if (MaterialManager.isLightingMat(matDef)) {

targetMat.setBoolean("Minnaert", true);

targetMat.getAdditionalRenderState().setBlendMode(BlendMode.Additive);

}

targetGeo.getMesh().setLineWidth(3f);



savedMat = targetMat.clone();

Material newMat = new MaterialManager(assetManager).convertToLightBlow(targetMat);

newMat.setBoolean("Toon", true);

newMat.setFloat("EdgeSize", 0.2f);

newMat.setColor("EdgesColor", ColorRGBA.Blue);

newMat.setBoolean("Fog_Edges", true);

newMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

newMat.getAdditionalRenderState().setWireframe(true);

targetGeo.setMaterial(newMat);





// tell the entity manager that is the current picked entity.

// rememeber the oldParentNode and the translation

// attach to characterLookNode

}



void doDeselected() {

// make it normal

Material targetMat = targetGeo.getMaterial();

MaterialDef matDef = targetMat.getMaterialDef();

//System.out.println(matDef.getAssetName() + matDef.getName());

if (MaterialManager.isLightingMat(matDef)) {

targetMat.setBoolean("Minnaert", false);

targetMat.getAdditionalRenderState().setBlendMode(BlendMode.Off);

}

if (MaterialManager.isLightBlowMat(matDef)) {

targetMat.setBoolean("Toon", false);

targetMat.setFloat("EdgeSize", 0f);

targetMat.setColor("EdgesColor", ColorRGBA.Blue);

targetMat.setBoolean("Fog_Edges", false);

targetMat.getAdditionalRenderState().setBlendMode(BlendMode.Off);

targetMat.getAdditionalRenderState().setWireframe(false);

}

}

}

[/java]



MaterialManager.java

[java]

package com.longyg.jme3.selection;



import com.jme3.asset.AssetManager;

import com.jme3.material.Material;

import com.jme3.material.MaterialDef;

import com.jme3.texture.Texture;



public class MaterialManager {

private AssetManager assetManager;



public MaterialManager(AssetManager assetManager) {

this.assetManager = assetManager;

}



public static boolean isLightBlowMat(MaterialDef matDef) {

return (matDef.getAssetName().endsWith("LightBlow.j3md"));

}



public static boolean isLightingMat(MaterialDef matDef) {

return (matDef.getAssetName().endsWith("Lighting.j3md"));

}



public Material convertToLightBlow(Material orgMat) {

// check if org is Lighting.jmd

if (isLightingMat(orgMat.getMaterialDef())) {

// construct the clone Mat



Material newMat = new Material(assetManager,

"MatDefs/LightBlow/LightBlow.j3md");

Texture diffuseMap = orgMat.getTextureParam("DiffuseMap")

.getTextureValue();

newMat.setTexture("DiffuseMap", diffuseMap);

return newMat;

} else {

return orgMat;

}

}

}

[/java]



SelectionApplication.java

[java]

package com.longyg.jme3.selection;



import com.jme3.app.SimpleApplication;

import com.jme3.collision.CollisionResult;

import com.jme3.collision.CollisionResults;

import com.jme3.font.BitmapText;

import com.jme3.input.KeyInput;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Ray;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial;

import com.jme3.scene.shape.Box;



public class SelectionApplication extends SimpleApplication {



Node selectables;

Geometry mark;

SelectionManager selectionManager;



@Override

public void simpleInitApp() {

initCrossHairs();

initKeys();



selectables = new Node("Selectables");

rootNode.attachChild(selectables);

selectables.attachChild(makeCube("a Dragon", -2f, 0f, 1f));

selectables.attachChild(makeCube("a tin can", 1f, -2f, 0f));

selectables.attachChild(makeCube("the Sheriff", 0f, 1f, -2f));

selectables.attachChild(makeCube("the Deputy", 1f, 0f, -4f));

selectables.attachChild(makeFloor());

selectables.attachChild(makeCharacter());

}



protected void initCrossHairs() {

guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");

BitmapText ch = new BitmapText(guiFont, false);

ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);

ch.setText("+");

ch.setLocalTranslation(settings.getWidth() / 2, settings.getHeight() / 2, 0);

guiNode.attachChild(ch);

}



private void initKeys() {

inputManager.addMapping("Select", new KeyTrigger(KeyInput.KEY_SPACE),

new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

inputManager.addListener(actionListener, "Select");

}



private ActionListener actionListener = new ActionListener() {



public void onAction(String name, boolean keyPressed, float tpf) {

if (name.equals("Select") && !keyPressed) {

CollisionResults results = new CollisionResults();

Ray ray = new Ray(cam.getLocation(), cam.getDirection());

selectables.collideWith(ray, results);

if (results.size() > 0) {

CollisionResult closest = results.getClosestCollision();

selectionManager = new SelectionManager(closest.getGeometry(), assetManager);

selectionManager.doSelected();

} else {

if (selectionManager != null) {

selectionManager.doDeselected();

}

}

}

}

};



protected Geometry makeCube(String name, float x, float y, float z) {

Box box = new Box(new Vector3f(x, y, z), 1, 1, 1);

Geometry cube = new Geometry(name, box);

Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

mat1.setColor("Color", ColorRGBA.randomColor());

cube.setMaterial(mat1);

return cube;

}



protected Geometry makeFloor() {

Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15);

Geometry floor = new Geometry("the Floor", box);

Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

mat1.setColor("Color", ColorRGBA.Gray);

floor.setMaterial(mat1);

return floor;

}



protected Spatial makeCharacter() {

Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");

golem.scale(0.5f);

golem.setLocalTranslation(-1.0f, -1.5f, -0.6f);

DirectionalLight sun = new DirectionalLight();

sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));

golem.addLight(sun);

return golem;

}



public static void main(String[] args) {

SelectionApplication app = new SelectionApplication();

app.start();

}

}

[/java]



In the scene, some cubes are attached to rootNode, i want to can select them.

I used Unshaded.j3md material, but it can not be converted.



Yes, Unshaded material can not be converted because my code don’t support it. For Unshaded material, just override the LightBlow mat instead. You know, the reason why I want to convert from matLightning → matLightBlow because I want to preserve the textures and some color attribute. As for Unshaded mat, you should just override it, not thing to convert!!!

@atomix, I changed my code to use Lighting material to the cubes, now it can go into the convert code. But I met an exception:



[java]java.lang.IllegalArgumentException: No enum const class com.jme3.shader.VarType.(MaterialShininess)

at java.lang.Enum.valueOf(Enum.java:196)

at com.jme3.shader.VarType.valueOf(VarType.java:35)

at com.jme3.material.plugins.J3MLoader.readParam(J3MLoader.java:164)

at com.jme3.material.plugins.J3MLoader.readMaterialParams(J3MLoader.java:269)

at com.jme3.material.plugins.J3MLoader.loadFromScanner(J3MLoader.java:572)

at com.jme3.material.plugins.J3MLoader.load(J3MLoader.java:589)

at com.jme3.asset.DesktopAssetManager.loadAsset(DesktopAssetManager.java:243)

at com.jme3.material.Material.<init>(Material.java:167)

at com.longyg.jme3.selection.MaterialManager.convertToLightBlow(MaterialManager.java:28)

at com.longyg.jme3.selection.SelectionManager.doSelected(SelectionManager.java:34)

at com.longyg.jme3.selection.SelectionApplication$1.onAction(SelectionApplication.java:88)

at com.jme3.input.InputManager.invokeActions(InputManager.java:143)

at com.jme3.input.InputManager.onMouseButtonEventQueued(InputManager.java:363)

at com.jme3.input.InputManager.processQueue(InputManager.java:565)

at com.jme3.input.InputManager.update(InputManager.java:600)

at com.jme3.app.Application.update(Application.java:453)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:223)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:158)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:203)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:221)

at java.lang.Thread.run(Thread.java:662)

[/java]



It seems the MaterialShininess parameter which defined in LightBlow is not supported by JME3 VarType?

@longyg you can add support of ToonEdge for unshaded shader.


  1. Paste to your own Material Defenition.

    http://code.google.com/p/jme-glsl-shaders/source/browse/assets/MatDefs/LightBlow/LightBlow.j3md#265 - LINES 265-320


  2. Paste these files to your folder project:

    http://code.google.com/p/jme-glsl-shaders/source/browse/#hg%2Fassets%2FShaders%2FToonBlow


  3. Paste to your project this Processor:

    http://code.google.com/p/jme-glsl-shaders/source/browse/src/LightBlow/CartoonEdgeProcessor.java



    Also you will need to get some variables from LightBlow.frag.

@mifth, Thanks for your info.

I didn’t use my own material definition, I used LightBlow.j3md directly.

In LightBlow.j3md, there is a parameter defined at 61 line:

[java]

Float Shininess (MaterialShininess) : 1 // Specular power/shininess

[/java]



But it will raise above exception:

[java]java.lang.IllegalArgumentException: No enum const class com.jme3.shader.VarType.(MaterialShininess)[/java]



If I comment this line in LightBlow.j3md, it will raise another exception:

[java]java.lang.NullPointerException: Material definition cannot be null[/java]



I’m a novice at Material…Could you please have a look at my issue?

Let try :

Float Shininess :1

did you enable Specular_Lighting parameter (make it true) in LightBlow?



Have a look at LightingSystem.java example.



JME matDef has the same parameters but Lightblow uses differently the specular.

http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md#46 - line 46 JME matDef

@atomix said:
Let try :
Float Shininess :1

@atomix, If change this line to Float Shininess:1 in LightBlow.j3md, the same exception raised:
[java]java.lang.NullPointerException: Material definition cannot be null[/java]
@mifth said:
did you enable Specular_Lighting parameter (make it true) in LightBlow?

Have a look at LightingSystem.java example.

JME matDef has the same parameters but Lightblow uses differently the specular.
http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md#46 - line 46 JME matDef


@mifth, I didn't change anything in LightBlow.j3md, just load it using assetManager:
[java]
Material newMat = new Material(assetManager, "MatDefs/LightBlow/LightBlow.j3md");
[/java]
The exception would raise at this line when load it:
[java]
java.lang.IllegalArgumentException: No enum const class com.jme3.shader.VarType.(MaterialShininess)
[/java]

So I try to comment out the line which contains word "MaterialShininess" in LightBlow.j3md and run again, the NullPointerException raised:
[java]java.lang.NullPointerException: Material definition cannot be null[/java]