Hey all! I’m relatively new to JMonkey and am trying to make a simple effect where when I click on a certain node, everything besides that node is blurred in the background. I’ve gotten to the point where I can click on an object and apply the DepthOfFieldFilter to the scene, but am unsure of how to proceed from here, or if a DepthOfFieldFilter is even what I want to be using.
I have this code flow so far:
- Create spatials, just simple text Box Geometries for now
- Add those spatials to an ‘interactive’ Node, where things can be clicked via Ray Casting
- When a spatial is clicked, give it a UserData value of ‘stateFocused = true’
- Call an AppState I named ‘FocusedAs’ (focused App State)
- In the AppState, get all nodes with UserData of ‘stateFocused = false’ and remove them from the ‘interactive’ Node, then add them to the ‘non-interactive’ Node, leaving only the one clicked on remaining in the ‘interactive’ Node
- Create a DepthOfFieldFilter in the ‘FocusedAs’ and add it to the Application class’s FilterPostProcessor
This successfully blurs the entire scene when clicking on a Spatial, and separates the two categories of Spatials into ‘interactive’ Nodes and ‘non-interactive’ Nodes, but I’m unsure of how to apply the blur effect to only the ‘non-interactive’ Nodes. I was reading about render passes, but can’t seem to find any examples of how they work in JMonkey. Any suggestions? Or is there an easy way to only apply a post processing filter to certain nodes?
I’ll post my code below, but it’s written in Kotlin (company standard), so I’m not sure how useful it will be, although Kotlin is very readable! I can convert it to Java if requested.
class MyApp : SimpleApplication(), ActionListener {
private val iNode = Node(Nodes.INODE.name) //just points to a String enum
private val niNode = Node(Nodes.NINODE.name) //just points to a String enum
private val focusedAS = FocusedAs() // my custom AppState class
val fpp = FilterPostProcessor(assetManager)
override fun simpleInitApp() {
viewPort.addProcessor(fpp)
createBoxes()
}
//ommitted some of my other functions like createBoxes()
//and initializing the input maps due to irrelevancy
override fun onAction(name: String, isPressed: Boolean, tpf: Float) {
if (name == Actions.CLICK.name && !isPressed) {
val results = CollisionResults()
val click2d = inputManager.cursorPosition.clone()
val click3d = cam.getWorldCoordinates(click2d, 0f).clone()
val dir = cam.getWorldCoordinates(click2d, 1f)
.subtractLocal(click3d).normalizeLocal()
val ray = Ray(click3d, dir)
iNode.collideWith(ray, results)
if (results.size() > 0) {
val closest = results.closestCollision
val selectedGeo = closest.geometry
selectedGeo.setUserData(Actions.FOCUSEDSTATE.name, true) //an enum pointing to a string
if (stateManager.hasState(focusedAS)) {
//already attached
val fas: FocusedAs? = stateManager.getState(FocusedAs::class.java)
fas?.isEnabled = true //enables the AppState if its already attached
} else {
//attach the appState
stateManager.attach(focusedAS)
}
}
}
}
}
and my AppState…
class FocusedAs : BaseAppState() {
override fun initialize(app: Application) {
//called when AppState is initialized
if (app is MyApp) {
val iNode = app.rootNode.getChild(MyApp.Nodes.INODE.name) as Node //the interactive Node
val niNode = app.rootNode.getChild(MyApp.Nodes.NINODE.name) as Node //the non-interactive Node
//find find the selected geometry
iNode.children.forEach {
if (!it.getUserData<Boolean>(MyApp.Actions.FOCUSEDSTATE.name)) {
iNode.detachChild(it) //remove spatials from activenode
niNode.attachChild(it) //add spatials to inactivenode
}
}
val selectedGeo = iNode.children[0] //only spatial remaining is the selected spatial
//blur the inactive node
val dofFilter = DepthOfFieldFilter()
dofFilter.focusDistance = 0f
dofFilter.focusRange = 0f
dofFilter.blurScale = 20f
app.fpp.addFilter(dofFilter)
//TODO: figure out how to apply this only to inactive node..
} else {
//TODO: Make more specific Exception
throw Exception("MyApp not found when trying to initialize FocusedAs.")
}
}
//didn't include rest of override functions due to irrelevancy
}
There we go! As I say, it’s working so far as blurring the spatials and moving them all to the right Nodes, but I can’t figure out how to blur only a select number of Nodes. Any help or documentation would be greatly appreciated! Thank you!
Edit: Oh, and if anyone could tell me how to increase the resolution of my blurred spatials, that would be phenomenal. They are very pixelated at the moment.
See how pixelated they are? Not sure how to fix this! I’m thinking I’ll have to do some GLSL shader work, but I’m not sure.
Edit2: I figured out how to make the resolution better– add more DepthOfFieldFilters on top of the original filter. Not sure if this is the best way to go about it, however…