Load and edit custom Controls in SceneComposer

Added file templates for creating new Control and AppState classes, they have a lot of comments by default that explain how the classes are used and where to add what code. Adding a Control should be an absolute no-brainer now :wink:

3 Likes

This is exactly for me!!! I don’t have a brain. THANKS!! :slight_smile:

@normen:

following scenario:

i have a interface:

[java]

public interface RessourceManagerControlActivatable extends Control{

public void onActivate();

public void onDeactivate();

}

[/java]



Now i want to implement different classes which implement this interface, for example LightControl which adds/removes lightning.

From the main ressourcehandle i can then call:

[java]getControl(RessourceManagerControlActivatable.class).onActivate();[/java]



So far so good. Is there any chance i can add such classes in the Scene Composer? Currently it shows me only the interface as option

All your projects classes are available to the Controls and you can add any control by specifying its name explicitly.

Cool, i didn’t tought i can add my own class to the list.



Perfect, ty

Hey @normen: I have a few questions regarding this system, i really like the idea of having controls added to a object in the scene editor. However, i would like to know how the constructor is called when i attach it. Especially i need access to the Node/Spatial the control is added and the StateManager (i have a main ressource handler state).



In code i usually have a contructor like:

TreeControl(Node tree, Core core); //core gives me access to the statemanager



How would this work with custom controls added in the scene editor?



Add: i always get a “Cannot find class: Controls.Objects.TreeControl” error, Name looks correct. (Save on compile is enabled) Class is in the build folder

Its not called, as for any serialization process an empty constructor is needed. Make sure the read() method reconstructs the object properly.

@normen said:
Its not called, as for any serialization process an empty constructor is needed. Make sure the read() method reconstructs the object properly.


I see. In the meantime i have seen that access to the spatial is not a problem. But how to get access to a AppState?

You think the wrong way around, an AppState is not part of the SceneGraph. The AppState in turn should load the models not the models an AppState.

@normen said:
You think the wrong way around, an AppState is not part of the SceneGraph. The AppState in turn should load the models not the models an AppState.


Could be, maybe i am thinking the wrong way, i explain what i try to reach:

I want to code a simple 'paging system'. The system includes a main RessourceHandler which distributes the Spatials. (Basically a HashMap<String,Stack>;

My idea was to make it an AppState so it should be accessible from anywhere.

RessourceHandler:
[java]
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package States;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.scene.Spatial;
import java.util.HashMap;
import java.util.Stack;

/**
*
* @author zzuegg
*/
public class RessourceManager extends AbstractAppState {
HashMap<String,Stack<Spatial>> ressources;
Core core;

static int ModePreloaded=1;
static int ModeOnDemand=2;


int mode=1;
int preLoadCount=100;

public void RessourceManager(){
this.ressources=new HashMap<String,Stack<Spatial>>();
}

public void registerObject(String name,String object){
this.ressources.put(name, new Stack<Spatial>());
Spatial item=core.getAssetManager().loadModel(object);

if(mode==RessourceManager.ModePreloaded){
for(int i=0;i<this.preLoadCount;i++){
this.ressources.get(name).push(item.clone());
}
}
if(mode==RessourceManager.ModeOnDemand){
this.ressources.get(name).push(item);
}
}

public void returnRessource(String name, Spatial item){
this.ressources.get(name).push(item);
}

public Spatial getRessource(String name){
Spatial item=null;
Stack<Spatial> stack=this.ressources.get(name);
if(stack==null) return null;
if(this.mode==RessourceManager.ModePreloaded){
if(stack.size()>0){
return stack.pop();
}else{
return null;
}
}
if(this.mode==RessourceManager.ModeOnDemand){
if(stack.size()>1){
return stack.pop();
}else{
if(stack.size()==1){
return stack.peek().clone();
}else{
return null;
}
}
}
return item;
}


@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);

}

@Override
public void update(float tpf) {
//TODO: implement behavior during runtime
}

@Override
public void cleanup() {
super.cleanup();
//TODO: clean up what you initialized in the initialize method,
//e.g. remove all spatials from rootNode
//this is called on the OpenGL thread after the AppState has been detached
}
}
[/java]

On the SceneGraph i attach a Node with added tree control:
The control requests a spatial when it is on the viewport and gives it back when it is not visible anymore.
Is that approach totaly wrong?

How you set it now? If you cannot get out of that trap… just set it after loading the model? e.g. [java]myLoadedModel.getControl(MyGreatControl.class).setNeededAppState(state);

[/java]

Normally I’d say an AppState handles attaching the models in the first place so thats why I say its “the wrong way round”. Inside the AppState it should be no problem accessing it :wink: Read through this thread, what you mean has been mentioned as well a how the system can be used.

@normen said:
How you set it now? If you cannot get out of that trap.. just set it after loading the model? e.g. [java]myLoadedModel.getControl(MyGreatControl.class).setNeededAppState(state);
[/java]
Normally I'd say an AppState handles attaching the models in the first place so thats why I say its "the wrong way round". Inside the AppState it should be no problem accessing it ;) Read through this thread, what you mean has been mentioned as well a how the system can be used.


Indeed that would be a solution, but i am not very happy with it since it would requĂ­re adding the needed state for each control. This thing i wanted to avoid by using the Scene Editors 'add control' feature.

I think i am going to implement the RessourceManager as singleton instead of an AppState.

You won’t be able to access that either.

Edit: Rather it will not be able to access other parts of your app (e.g. an assetManager you set in the main application, that doesn’t happen in the SceneComposer). The idea is to set data on these Controls or prepare them for each model in a way so that you don’t have to do that in the AppState by using an xml file or list or something. Things like MyFightingControl.setWeaonStrength(), not stuff that is dependent on the other parts of the game.

@normen said:
You won't be able to access that either.
Edit: Rather it will not be able to access other parts of your app (e.g. an assetManager you set in the main application, that doesn't happen in the SceneComposer). The idea is to set data on these Controls or prepare them for each model in a way so that you don't have to do that in the AppState by using an xml file or list or something. Things like MyFightingControl.setWeaonStrength(), not stuff that is dependent on the other parts of the game.


Why not? on the singleton i only have to call
[java]RessourceManager.getInstance().setAssetManager(assetManager); [/java]
once, afterwards it will have all neccessary informations it would need. From inside my control i now can fully access the ressourcemanager for requesting models without need to modify or set some variables.

But i fully understand you, but i see the ressourcehandling as extension of LodControl
@zzuegg said:
Why not? on the singleton i only have to call
[java]RessourceManager.getInstance().setAssetManager(assetManager); [/java]
once, afterwards it will have all neccessary informations it would need. From inside my control i now can fully access the ressourcemanager for requesting models without need to modify or set some variables.

But i fully understand you, but i see the ressourcehandling as extension of LodControl

Uhm, where you'd set that? The assetManager variable has to come from somewhere.. Anyway the *assetManager* is available in the read() method, through the JmeImporter that is passed to the read() method.

In the simpleInitApp() i have:

[java]

RessourceManager.getInstance().setAssetManager(assetManager);

loadScene();

[/java]



The loadScene function loads a scene with terrain and a lot of empty nodes with the TreeControl() added.

The TreeControl() includes following code:

[java] protected void controlUpdate(float tpf) {

if(this.hasRessource){

if(!this.needRessource){

this.ressource.removeFromParent();

RessourceManager.getInstace().returnRessource(spatial.getName(), ressource);

}

}else{

if(this.needRessource){

ressource=RessourceManager.getInstace().getRessource(spatial.getName());

if(this.ressource!=null){

((Node)this.getSpatial()).attachChild(ressource);

}

}

}



}[/java]



Which should load and return the needed model fine. Currently i cannot see a problem, but we will see once i reach a state where i can test this :wink:

And as I said, that simpleInitApp() will never be called for the stuff you load in the SceneComposer.

@normen said:
And as I said, that simpleInitApp() will never be called for the stuff you load in the SceneComposer.


I fully understand that. But the ressourceHandle should work ingame not in the scene composer. Maybe i forgot to mention that.

Again, when its just about the assetManager, you can get that in the read() method. When you need static accessors for communication between code parts in your application that hints that your application design might need some improvements really.

I have some additional question about this. I get the PhysicsSpace from the constructor in my Controls. Should I get this from the read() method too ?