package logic.nodes.lod;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.Callable;
import logic.nodes.collision.CollidableNode;
import logic.nodes.collision.DummyType;
import logic.nodes.lod.blocks.LeafBlock;
import logic.ships.mothership.MotherShip;
import logic.weapons.Weapon;
import settings.GraphicSettings;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingVolume;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.BillboardNode;
import com.jme.scene.DistanceSwitchModel;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.lod.DiscreteLodNode;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.RenderState.StateType;
import com.jme.util.GameTaskQueueManager;
import fileHandling.ModelImporter;
import fileHandling.TextureLoader;
import gameStates.absGamesStates.AbsIngameState;
public abstract class LODNode extends CollidableNode {
private static final long serialVersionUID = 1L;
protected String geomQual, texQual, normQual, specQual, bumpQual, shaderQual;
protected float currentLOD;
protected Collection<Node> detachedSlots, lodNodes;
protected HashMap<TriMesh, BoundingVolume> meshBounds;
protected HashMap<TriMesh, Vector3f> meshDirs;
private int maxSwitches;
public LODNode(String path, String title, Node model, AbsIngameState ingameState) {
this(path, title, model, DummyType.None, ingameState);
}
public LODNode(String path, String title, Node model, DummyType dummyType, AbsIngameState ingameState) {
super(path, title, model, dummyType, ingameState);
updateGeometryQuality();
}
public boolean updateGeometryQuality() {
if(geomQual != null &&
geomQual.equals(GraphicSettings.get().getGeometryQuality())
&& currentLOD == GraphicSettings.get().getLOD()) return false;
setGraphicSettings();
Node newModel = ModelImporter.getModel(path);
setModel(getLODNode(newModel));
assert(getModel() != null);
updateRenderState();
if(isLarge()) {
int childIndex = 2;
if(geomQual.equals("medium")) childIndex = 1;
else if(geomQual.equals("low")) childIndex = 0;
getLODModel().setActiveChild(childIndex);
meshBounds = new HashMap<TriMesh, BoundingVolume>();
meshDirs = new HashMap<TriMesh, Vector3f>();
getTriMeshes((Node)getLODModel().getChild(childIndex));
}
return true;
}
public void removeBoundControllerOf(TriMesh mesh) {
boundControllers.remove(mesh);
lockNode();
}
protected void getTriMeshes(Node parent) {
for(Spatial child : parent.getChildren()) {
if(child instanceof Node) getTriMeshes((Node)child);
else if(child instanceof TriMesh) {
TriMesh mesh = (TriMesh)child;
mesh.setModelBound(new BoundingBox());
mesh.updateModelBound();
mesh.updateWorldBound();
TriMeshBoundController c = new TriMeshBoundController(mesh, this, meshBounds, meshDirs);
boundControllers.put(mesh, c);
mesh.addController(c);
}
}
}
protected DiscreteLodNode getLODNode(Node maxQualModel) {
String geomQual = GraphicSettings.get().getGeometryQuality();
maxSwitches = 6;
if(geomQual.equals("medium")) maxSwitches = 5;
else if(geomQual.equals("low")) maxSwitches = 4;
detachedSlots = new HashSet<Node>();
lodNodes = new ArrayList<Node>();
DistanceSwitchModel switchModel = new DistanceSwitchModel(maxSwitches);
DiscreteLodNode dlodNode = getDiscreteLodNode(switchModel);
float notVisibleDist = LeafBlock.VIEW_DIST - LeafBlock.MIN_LOD_VIEW_DIST;
float visibleDist = LeafBlock.MIN_LOD_VIEW_DIST + (notVisibleDist * GraphicSettings.get().getLOD());
float distDiv = visibleDist / maxSwitches;
float dist = distDiv * getDistanceFactor();
assert(dist > 0f);
String texQual = GraphicSettings.get().getTextureQuality();
setScaleOf(maxQualModel);
updateTextureQualityOf(maxQualModel, texQual);
dlodNode.attachChild(maxQualModel);
switchModel.setModelDistance(0, 0f, dist);
detachedSlots.add(maxQualModel);
lodNodes.add(maxQualModel);
int lastSwitch = maxSwitches;
boolean isMS = this instanceof MotherShip;
boolean useBB = !(this instanceof Weapon || isMS);
boolean lastTillInf = isMS;
for(int i = 1; i <= lastSwitch ; i++) {
String newTexQual = ModelImporter.nextLevel(texQual, true);
Node node = null;
if(i != lastSwitch) {
if(newTexQual != null) texQual = newTexQual;
geomQual = ModelImporter.nextLevel(geomQual, true);
assert(geomQual != null);
float maxDist = (i + 1) * dist;
if(i == lastSwitch - 1 && lastTillInf) maxDist = Float.POSITIVE_INFINITY;
switchModel.setModelDistance(i, i * dist, maxDist);
node = ModelImporter.getModelOfQuality(path, geomQual);
assert(node != null);
setScaleOf(node);
updateTextureQualityOf(node, texQual);
detachedSlots.add(node);
} else if(useBB) {
node = new Node("Dummy_BBNode");
node.attachChild(new Quad("Dummy_Quad"));
switchModel.setModelDistance(i, i * dist, Float.POSITIVE_INFINITY);
addController(new BBNodeController(this));
} else break;
dlodNode.attachChildAt(node, i);
lodNodes.add(node);
if(i == lastSwitch - 1 && lastTillInf) break;
}
assert(ModelImporter.nextLevel(geomQual, true) == null);
return dlodNode;
}
public void setBillboardNode(BillboardNode bbNode) {
getLODModel().attachChildAt(bbNode, maxSwitches);
}
public void updateTextureFilters() {
for(Node node : lodNodes) {
updateTextureFilters(node);
}
updateRenderState();
}
protected void updateTextureFilters(Node model) {
if(model == null || model.getChildren() == null) return;
for(Spatial child : model.getChildren()) {
if(child instanceof Node) updateTextureFilters((Node)child);
else {
TextureState ts = (TextureState)child.getRenderState(StateType.Texture);
if(ts == null) continue;
Texture tex = ts.getTexture();
if(tex == null) continue;
TextureLoader.upateTextureFilter(tex);
}
}
}
public boolean updateTextureQuality() {
if(texQual != null &&
texQual.equals(GraphicSettings.get().getTextureQuality()) &&
normQual != null &&
normQual.equals(GraphicSettings.get().getNormalQuality()) &&
specQual != null &&
specQual.equals(GraphicSettings.get().getSpecularQuality()) &&
bumpQual != null &&
bumpQual.equals(GraphicSettings.get().getBumpQuality()) &&
shaderQual != null &&
shaderQual.equals(GraphicSettings.get().getShaderQuality())) return false;
setGraphicSettings();
String tempQual = texQual;
for(Node child : lodNodes) {
updateTextureQualityOf(child, tempQual);
String newTexQual = ModelImporter.nextLevel(tempQual, true);
if(newTexQual != null) tempQual = newTexQual;
}
updateRenderState();
return true;
}
protected void setGraphicSettings() {
GraphicSettings settings = GraphicSettings.get();
geomQual = settings.getGeometryQuality();
currentLOD = settings.getLOD();
texQual = settings.getTextureQuality();
normQual = settings.getNormalQuality();
specQual = settings.getSpecularQuality();
bumpQual = settings.getBumpQuality();
shaderQual = settings.getShaderQuality();
}
protected void updateTextureQualityOf(Node model, String quality) {
assert(quality != null && model != null && model.getChildren() != null);
for(Spatial child : model.getChildren()) {
if(child instanceof Node && ((Node)child).getChildren() != null)
updateTextureQualityOf((Node)child, quality);
else if(child instanceof TriMesh) {
TextureLoader.updateTextureQuality((TriMesh)child, quality);
child.updateRenderState();
}
}
}
public Collection<TriMesh> getTriMeshes() { return meshBounds.keySet(); }
public BoundingVolume getTriMeshBounding(TriMesh mesh) { return meshBounds.get(mesh); }
public Vector3f getTriMeshDir(TriMesh mesh) { return meshDirs.get(mesh); }
public DiscreteLodNode getLODModel() { return (DiscreteLodNode)getModel(); }
public Node getActiveLODModel() {
DiscreteLodNode lod = getLODModel();
return (Node)lod.getChild(lod.getActiveChild());
}
protected DiscreteLodNode getDiscreteLodNode(DistanceSwitchModel switchModel) {
return new DiscreteLodNode("LOD-Node", switchModel);
}
protected float getDistanceFactor() { return 1f; }
public float getDefaultScale() { return 0.08f; }
protected void setScaleOf(Spatial child) {
child.setLocalScale(getDefaultScale());
}
public void updateGraphicSettings() {
try {
GameTaskQueueManager.getManager().update(new Callable<Void>() {
@Override
public Void call() throws Exception {
if(!updateGeometryQuality() && !updateTextureQuality()) updateTextureFilters();
return null;
}
}).get();
} catch(Exception e) {}
}
}
|