package org.multiverseking.render;
import com.jme3.collision.CollisionResults;
import com.jme3.math.Ray;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import com.simsilica.es.Entity;
import com.simsilica.es.EntityId;
import com.simsilica.es.EntitySet;
import java.util.ArrayList;
import java.util.HashMap;
import org.multiverseking.core.utility.EntitySystemAppState;
import org.multiverseking.core.utility.RootSystem;
import org.multiverseking.core.utility.SubSystem;
import org.multiverseking.render.utility.SpatialInitializer;
import org.slf4j.LoggerFactory;
/**
* Handle the render of all entities, spatial loading parenting etc... /!\ Does
* not handle the positioning of entity. HexGrid got his own render system.
*
* @author Eike Foede, roah
*/
public class RenderSystem extends EntitySystemAppState implements RootSystem {
private final Node renderSystemNode = new Node("RenderSystemNode");
private HashMap<EntityId, Spatial> spatials = new HashMap<>();
private ArrayList<SubSystem> subSystems = new ArrayList<>(3);
private SpatialInitializer spatialInitializer;
// <editor-fold defaultstate="collapsed" desc="Entity System">
@Override
protected EntitySet initialiseSystem() {
app.getRootNode().attachChild(renderSystemNode);
renderSystemNode.setShadowMode(RenderQueue.ShadowMode.Cast); //<< diseable this to remove the shadow. -50%fps...
spatialInitializer = new SpatialInitializer(app.getAssetManager(), "Models");
return entityData.getEntities(RenderComponent.class);
}
@Override
protected void updateSystem(float tpf) {
}
@Override
public void addEntity(Entity e) {
spatials.put(e.getId(), initialiseSpatial(e));
Spatial parent = spatials.get(e.get(RenderComponent.class).getParent());
if (parent != null) {
((Node) parent).attachChild(spatials.get(e.getId()));
} else if (getSubSystemNode(e.get(RenderComponent.class).getSubSystem()) != null) {
addSpatialToSubSystem(e.getId(), e.get(RenderComponent.class).getSubSystem());
} else {
renderSystemNode.attachChild(spatials.get(e.getId()));
}
Control[] control = e.get(RenderComponent.class).getControl();
if (control != null) {
Spatial s = spatials.get(e.getId());
for (Control c : control) {
s.addControl(c);
}
}
if (!e.get(RenderComponent.class).isVisible()) {
spatials.get(e.getId()).setCullHint(Spatial.CullHint.Always);
}
}
@Override
protected void updateEntity(Entity e) {
Spatial s = spatials.get(e.getId());
/**
* Update the spatial geometry if it need to.
*/
String eName = computeSpatialName(e);
if (!eName.equals(s.getName())) {
switchSpatial(e);
}
/**
* Update the spatial parenting
*/
RenderComponent renderComp = e.get(RenderComponent.class);
Spatial renderCompParent = spatials.get(renderComp.getParent());
if (renderCompParent != null && !s.getParent().getName().equals(renderCompParent.getName())) {
((Node) renderCompParent).attachChild(spatials.get(e.getId()));
} else if (renderCompParent != null && s.getParent().getName().equals(renderCompParent.getName())) {
// If the parent hasn't change do nothing
} else if (e.get(RenderComponent.class).getSubSystem() != null
&& getSubSystemNode(e.get(RenderComponent.class).getSubSystem()) != null) {
// If the subSystem Node is currently processed.
if (getSubSystemNode(e.get(RenderComponent.class)
.getSubSystem()).getName().equals(s.getParent().getName())) {
// If the SubSystem is the same than the parent do nothing
} else {
// If the spatial parent is not the subSystem node
addSpatialToSubSystem(e.getId(), e.get(RenderComponent.class).getSubSystem());
}
} else {
renderSystemNode.attachChild(spatials.get(e.getId()));
}
// if (s.getParent() == null && entityRender.getParent() != null
// && !spatials.containsKey(entityRender.getParent())) {
// // We attach the spatial to his parent if currently processed
// ((Node) spatials.get(entityRender.getParent())).attachChild(s);
// } else if ((s.getParent() == null
// // If the spatial is not attach to the scene
// // We attach the Spatial to his corresponding system
// // Else it is added to the render Node
// || (entityRender.getParent() != null
// && !spatials.containsKey(entityRender.getParent())))
// && !addSpatialToSubSystem(e.getId(), entityRender.getSubSystem())) {
// // If the spatial is attach to the scene but the parenting has been change
// // and the new parent is not currently processed we add it to the
// // corresponding System else on his the render Node
// addSpatialToRenderNode(e.getId());
// } else if () {
// // we check if the parent id is not currently processed
// } else if ( && !computeSpatialName((Entity) entityData.getEntity(
// entityRender.getParent(), RenderComponent.class))
// .equals(s.getParent().getName())) {
// // if the current parent Spatial Id is not the same than render component Parent Spatial ID
// } else {
// }
/**
* Update the spatial visibility if it need to.
*/
if (renderComp.isVisible()) {
s.setCullHint(Spatial.CullHint.Inherit);
} else {
s.setCullHint(Spatial.CullHint.Always);
}
}
private String computeSpatialName(Entity e) {
return e.get(RenderComponent.class).getName() + e.get(RenderComponent.class).getRenderType() + e.getId().toString();
}
@Override
public void removeEntity(Entity e) {
spatials.get(e.getId()).removeFromParent();
spatials.remove(e.getId());
}
@Override
public void cleanupSubSystem() {
for (SubSystem s : subSystems) {
s.rootSystemIsRemoved();
}
}
@Override
protected void cleanupSystem() {
cleanupSubSystem();
spatials.clear();
renderSystemNode.removeFromParent();
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="SubSystem Method">
/**
* Register a System to work with entity Spatial. (No node is added for that
* system)
*/
@Override
public void registerSubSystem(SubSystem system) {
registerSubSystem(system, false);
}
/**
* Register a system to work with spatial having his own node settup.
*
* @param subSystem The system to register.
* @param addNode Set it to true if the system require to do some operation
* on his own.
* @return A generated node for the system to work with if requested else
* null.
*/
public Node registerSubSystem(SubSystem subSystem, boolean addNode) {
Node n = null;
if (subSystems.contains(subSystem)) {
n = (Node) renderSystemNode.getChild(subSystem.getClass().getName());
} else {
subSystems.add(subSystem);
}
if (addNode && n == null) {
n = new Node(subSystem.getClass().getName());
renderSystemNode.attachChild(n);
for (Entity e : entities) {
if (e.get(RenderComponent.class).getSubSystem() != null
&& e.get(RenderComponent.class).getSubSystem().equals(subSystem)) {
n.attachChild(spatials.get(e.getId()));
}
}
} else if (addNode && n != null && !n.getName().equals(subSystem.getClass().getName())) {
n.setName(subSystem.getClass().getName());
} else if (!addNode && n != null) {
renderSystemNode.detachChild(n);
}
return n;
}
/**
* Remove a subSystem ratached to this system. Clearing all spatial that
* belong to that subSystem.
*
* @param subSystem the subSystem to remove.
*/
@Override
public void removeSubSystem(SubSystem subSystem) {
removeSubSystem(subSystem, true);
}
/**
* Remove a subSystem ratached to this system.
*
* @param subSystem the subSystem to remove.
* @param clear does all spatial belong to that subSystem have to be deleted
* ?
*/
public void removeSubSystem(SubSystem subSystem, boolean clear) {
if (subSystems.remove(subSystem)) {
Node n = (Node) renderSystemNode.getChild(subSystem.getClass().getName());
if (n != null && !clear) {
for (Spatial s : n.getChildren()) {
if (spatials.containsValue(s)) {
renderSystemNode.attachChild(s);
}
}
} else if (n != null) {
renderSystemNode.detachChild(n);
}
} else {
LoggerFactory.getLogger(this.getClass()).warn(
"Trying to remove a subSystem not referenced : {} ",
new Object[]{subSystem.getClass().getName()});
}
}
public Node getSubSystemNode(SubSystem subSystem) {
if(subSystem != null) {
if (subSystems.contains(subSystem)) {
Node n = (Node) renderSystemNode.getChild(subSystem.getClass().getName());
if (n != null) {
return n;
} else {
LoggerFactory.getLogger(this.getClass()).info(
"No Node referenced for : {} ",
new Object[]{subSystem.getClass().getName()});
}
} else {
LoggerFactory.getLogger(this.getClass()).warn(
"Trying to get a Node for a subSystem not referenced : {1} ",
new Object[]{subSystem.getClass().getName()});
}
}
return null;
}
/**
* @deprecated use {@link #getSubSystemNode(org.multiverseking.SubSystem) }
*/
public CollisionResults subSystemCollideWith(SubSystem subSystem, Ray ray) {
if (subSystems.contains(subSystem)) {
CollisionResults result = new CollisionResults();
((Node) renderSystemNode.getChild(subSystem.getClass().getName())).collideWith(ray, result);
return result;
}
return null;
}
// </editor-fold>
private Spatial initialiseSpatial(Entity e) {
RenderComponent renderComp = e.get(RenderComponent.class);
Spatial s = spatialInitializer.initialize(renderComp.getName(), renderComp.getRenderType());
s.setName(computeSpatialName(e));
if (renderComp.getRenderType().equals(RenderComponent.RenderType.Debug)) {
s.setShadowMode(RenderQueue.ShadowMode.Cast);
} else {
s.setShadowMode(RenderQueue.ShadowMode.Inherit);
}
return s;
}
private void switchSpatial(Entity e) {
Spatial newSpatial = initialiseSpatial(e);
spatials.get(e.getId()).getParent().attachChild(newSpatial);
if (!e.get(RenderComponent.class).isVisible()) {
}
spatials.get(e.getId()).removeFromParent();
spatials.put(e.getId(), newSpatial);
}
private boolean addSpatialToSubSystem(EntityId id, SubSystem system) {
if (system == null) {
return false;
}
Node subSystemNode = (Node) renderSystemNode.getChild(system.getClass().getName());
subSystemNode.attachChild(spatials.get(id));
return true;
}
private void addSpatialToRenderNode(EntityId id) {
if (spatials.get(id) != null) {
renderSystemNode.attachChild(spatials.get(id));
}
}
/**
* Return the Spatial control of an entity, if any.
*
* @param id of the entity.
* @param control the control to add.
* @return false if there is no spatial for that entity.
*/
public boolean setControl(EntityId id, Control control) {
if (spatials.containsKey(id)) {
spatials.get(id).addControl(control);
return true;
}
return false;
}
/**
* Return the control of the define entity spatial if any.
*
* @param id of the entity.
* @return null if no control is found.
*/
public <T extends Control> T getControl(EntityId id, Class<T> controlType) {
if (spatials.containsKey(id)) {
Spatial s = spatials.get(id);
return searchControl(s, controlType);
}
return null;
}
private <T extends Control> T searchControl(Spatial s, Class<T> controlType) {
T ctrl = s.getControl(controlType);
if (ctrl == null && s instanceof Node && !((Node) s).getChildren().isEmpty()) {
for (Spatial child : ((Node) s).getChildren()) {
ctrl = searchControl(child, controlType);
// ctrl = child.getControl(controlType);
if (ctrl != null) {
return ctrl;
}
}
}
return ctrl;
}
public String getRenderNodeName() {
return renderSystemNode.getName();
}
public String getSpatialName(EntityId id) {
return spatials.get(id).getName();
}
public Spatial getSpatial(EntityId id) {
return spatials.get(id);
}
}