package de.venjinx.editor.debug;
import java.nio.FloatBuffer;
import java.util.HashSet;
import jme3tools.optimize.GeometryBatchFactory;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.font.BitmapText;
import com.jme3.math.Triangle;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.shape.Line;
import com.jme3.scene.shape.Sphere;
import de.venjinx.core.VoxelQuadTree;
import de.venjinx.core.VoxelQuadTree.QuadCases;
import de.venjinx.jme3.VoxelObjectNode;
import de.venjinx.jme3.scenegraph.ScenegraphNode;
import de.venjinx.util.Util;
public class DebugNode extends ScenegraphNode {
private static class Voxel extends Geometry {
private float voxelValue;
private Vector3f position;
private BitmapText valueText;
public Voxel(Vector3f pos, float value, float radius) {
super("VoxelGeometry" + pos + " " + value);
voxelValue = value;
position = pos;
mesh = new Sphere(4, 4, radius);
if (value < 0)
setMaterial(DebugMaterials.blackMat);
else setMaterial(DebugMaterials.whiteMat);
setLocalTranslation(pos);
valueText = new BitmapText(DebugMaterials.guiFont, false);
valueText.setName(pos.toString());
valueText.setSize(radius);
int lvl = 0;
while ((value % Math.pow(2, lvl)) != 0)
lvl--;
valueText.setText((int) (value / Math.pow(2, lvl)) + "x2^" + lvl);
valueText.addControl(DebugMaterials.bc.cloneForSpatial(valueText));
valueText.setLocalTranslation(pos.add(0, radius * 2, 0));
}
@Override
public int hashCode() {
int hash = 37;
hash += (37 * hash) + Float.floatToIntBits(position.x);
hash += (37 * hash) + Float.floatToIntBits(position.y);
hash += (37 * hash) + Float.floatToIntBits(position.z);
hash += (37 * hash) + Float.floatToIntBits(voxelValue);
return hash;
}
@Override
public boolean equals(Object o) {
if (o instanceof Voxel)
return position.equals(((Voxel) o).position)
&& (voxelValue == ((Voxel) o).voxelValue);
else return false;
}
}
private Geometry refGeometry;
private Mesh refMesh;
private Node refParent;
private VoxelObjectNode voxelObject;
private Geometry bound;
private ScenegraphNode normals;
private ScenegraphNode tangents;
private ScenegraphNode binormals;
private ScenegraphNode wire;
private ScenegraphNode voxelGrid;
private boolean showBound = false;
private boolean showNormals = false;
private boolean showWire = false;
private boolean showVoxelGrid = false;
public DebugNode(Geometry geometry) {
setName("Debug '" + geometry.getName() + "'");
refGeometry = geometry;
refMesh = geometry.getMesh();
refParent = geometry.getParent();
if (geometry.getName().contains("Voxel")) {
while (!(refParent instanceof VoxelObjectNode))
refParent = refParent.getParent();
voxelObject = (VoxelObjectNode) refParent;
}
}
public void showBound() {
showBound = !showBound;
if (showBound) {
if (bound == null)
bound = buildBound(refGeometry);
attachChild(bound);
} else detachChild(bound);
}
public void showNormals() {
showNormals = !showNormals;
if (showNormals) {
if (normals == null) {
if (refMesh.getBuffer(Type.Position) != null) {
normals = buildNormals(refGeometry);
normals.setLocalTranslation(refGeometry.getLocalTranslation());
}
if (refMesh.getBuffer(Type.Tangent) != null) {
tangents = buildTangents(refGeometry);
tangents.setLocalTranslation(refGeometry.getLocalTranslation());
}
if (refMesh.getBuffer(Type.Binormal) != null) {
binormals = buildBinormals(refGeometry);
binormals.setLocalTranslation(refGeometry.getLocalTranslation());
}
}
attachChild(normals);
attachChild(tangents);
attachChild(binormals);
} else {
detachChild(normals);
detachChild(tangents);
detachChild(binormals);
}
}
public void showWireframe() {
showWire = !showWire;
if (showWire) {
if (wire == null)
if (refMesh.getMode() == Mode.Triangles) {
wire = buildWireframe(refGeometry);
wire.setLocalTranslation(refGeometry.getLocalTranslation());
}
attachChild(wire);
} else detachChild(wire);
}
public void showVoxelGrid() {
if (voxelObject != null) {
showVoxelGrid = !showVoxelGrid;
if (showVoxelGrid) {
if (voxelGrid == null) {
voxelGrid = buildVoxelGrid(voxelObject);
voxelGrid.setLocalTranslation(voxelObject.getLocalTranslation());
}
attachChild(voxelGrid);
} else detachChild(voxelGrid);
}
}
public static ScenegraphNode buildDebugObjects(Geometry refGeom) {
ScenegraphNode node = new ScenegraphNode("Debug '" + refGeom.getName() + "'");
ScenegraphNode normals;
ScenegraphNode tangents;
ScenegraphNode binormals;
ScenegraphNode wire;
ScenegraphNode voxelGrid;
if ((refGeom != null) && (refGeom.getMesh() != null)) {
node.attachChild(buildBound(refGeom));
if (refGeom.getMesh().getBuffer(Type.Position) != null) {
normals = buildNormals(refGeom);
normals.setLocalTranslation(refGeom.getLocalTranslation());
node.attachChild(normals);
}
if (refGeom.getMesh().getBuffer(Type.Tangent) != null) {
tangents = buildTangents(refGeom);
tangents.setLocalTranslation(refGeom.getLocalTranslation());
node.attachChild(tangents);
}
if (refGeom.getMesh().getBuffer(Type.Binormal) != null) {
binormals = buildBinormals(refGeom);
binormals.setLocalTranslation(refGeom.getLocalTranslation());
node.attachChild(binormals);
}
if (refGeom.getMesh().getMode() == Mode.Triangles) {
wire = buildWireframe(refGeom);
wire.setLocalTranslation(refGeom.getLocalTranslation());
node.attachChild(wire);
}
if (refGeom.getName().contains("Voxel")) {
VoxelObjectNode vo;
Node parent = refGeom.getParent();
while (!(parent instanceof VoxelObjectNode))
parent = parent.getParent();
vo = (VoxelObjectNode) parent;
voxelGrid = buildVoxelGrid(vo);
voxelGrid.setLocalTranslation(vo.getLocalTranslation());
node.attachChild(voxelGrid);
vo.showChunkLODs();
vo.showChunkBounds();
}
}
return node;
}
public static Geometry buildBound(Geometry refGeom) {
BoundingVolume boundVolume = refGeom.getModelBound();
Geometry g;
switch (boundVolume.getType()) {
case AABB:
BoundingBox bBox = (BoundingBox) boundVolume;
g = Util.createBox(bBox.getMax(null).subtract(bBox.getMin(null)));
g.setLocalTranslation(bBox.getMin(null));
g.setName("Bound");
g.setMaterial(DebugMaterials.boundMat);
g.getMesh().setLineWidth(2f);
return g;
case Sphere:
BoundingSphere bSphere = (BoundingSphere) boundVolume;
Sphere s = new Sphere(8, 8, bSphere.getRadius());
s.setMode(Mode.Lines);
g = new Geometry("Bound", s);
g.setMaterial(DebugMaterials.boundMat);
g.setLocalRotation(refGeom.getWorldRotation());
return g;
default:
return null;
}
}
public static ScenegraphNode buildNormals(Geometry refGeom) {
Geometry g;
Arrow a;
ScenegraphNode normals = new ScenegraphNode("Normals");
Vector3f dir = new Vector3f();
FloatBuffer nB = refGeom.getMesh().getFloatBuffer(Type.Normal);
FloatBuffer vB = refGeom.getMesh().getFloatBuffer(Type.Position);
for (int i = 0; i < nB.limit(); i += 3) {
dir.set(nB.get(i), nB.get(i + 1), nB.get(i + 2));
a = new Arrow(dir);
a.setLineWidth(3f);
g = new Geometry("Normal" + dir, a);
g.scale(.1f);
g.setLocalTranslation(vB.get(i), vB.get(i + 1), vB.get(i + 2));
g.setMaterial(DebugMaterials.normalMat);
normals.attachChild(g);
}
// GeometryBatchFactory.optimize(normals);
return normals;
}
public static ScenegraphNode buildTangents(Geometry refGeom) {
Geometry g;
Arrow a;
ScenegraphNode tangents = new ScenegraphNode("Tangents");
Vector3f dir = new Vector3f();
FloatBuffer tB = refGeom.getMesh().getFloatBuffer(Type.Tangent);
FloatBuffer vB = refGeom.getMesh().getFloatBuffer(Type.Position);
int posFix = 0;
for (int i = 0; i < tB.limit(); i += 4) {
dir.set(tB.get(i), tB.get(i + 1), tB.get(i + 2)).normalizeLocal();
a = new Arrow(dir);
a.setLineWidth(2f);
g = new Geometry("Tangent" + dir, a);
g.scale(.1f);
g.setLocalTranslation(vB.get(i - posFix), vB.get((i - posFix) + 1),
vB.get((i - posFix) + 2));
g.setMaterial(DebugMaterials.tangentMat);
tangents.attachChild(g);
posFix++;
}
// GeometryBatchFactory.optimize(tangents);
return tangents;
}
public static ScenegraphNode buildBinormals(Geometry refGeom) {
Geometry g;
Arrow a;
ScenegraphNode binormals = new ScenegraphNode("Binormals");
Vector3f dir = new Vector3f();
FloatBuffer bnB = refGeom.getMesh().getFloatBuffer(Type.Binormal);
FloatBuffer vB = refGeom.getMesh().getFloatBuffer(Type.Position);
for (int i = 0; i < bnB.limit(); i += 3) {
dir.set(bnB.get(i), bnB.get(i + 1), bnB.get(i + 2));
a = new Arrow(dir);
g = new Geometry("Binormal" + dir, a);
g.scale(.1f);
g.setLocalTranslation(vB.get(i), vB.get(i + 1), vB.get(i + 2));
g.setMaterial(DebugMaterials.binormalMat);
binormals.attachChild(g);
}
// GeometryBatchFactory.optimize(binormals);
return binormals;
}
public static ScenegraphNode buildWireframe(Geometry refGeom) {
Line line;
Triangle tri;
Geometry g;
ScenegraphNode tris = new ScenegraphNode("Wireframe");
for (int i = 0; i < refGeom.getMesh().getTriangleCount(); i++) {
tri = new Triangle();
refGeom.getMesh().getTriangle(i, tri);
// bc = new BillboardControl();
// helloText = new BitmapText(guiFont, false);
// helloText.setSize(.05f);
// helloText.setText("tri" + i);
// helloText.setLocalTranslation(tri.getCenter());
// helloText.addControl(bc);
// myApp.getRootNode().attachChild(helloText);
line = new Line(tri.get1(), tri.get2());
g = new Geometry("Line" + tri.get1() + "->" + tri.get2(), line);
g.setMaterial(DebugMaterials.wireMat);
tris.attachChild(g);
line = new Line(tri.get2(), tri.get3());
g = new Geometry("Line" + tri.get2() + "->" + tri.get3(), line);
g.setMaterial(DebugMaterials.wireMat);
tris.attachChild(g);
line = new Line(tri.get3(), tri.get1());
g = new Geometry("Line" + tri.get3() + "->" + tri.get1(), line);
g.setMaterial(DebugMaterials.wireMat);
tris.attachChild(g);
}
GeometryBatchFactory.optimize(tris);
return tris;
}
public static Node buildVertIds(Geometry refGeom) {
Node node = new Node("vertIds");
BitmapText vertId;
Vector3f pos = new Vector3f();
FloatBuffer fb = refGeom.getMesh().getFloatBuffer(Type.Position);
for (int i = 0; i < fb.capacity(); i += 3) {
pos.set(fb.get(i), fb.get(i + 1), fb.get(i + 2));
vertId = new BitmapText(DebugMaterials.guiFont);
vertId.setSize(.1f);
vertId.setText("v " + (i / 3) + " " + pos);
vertId.setName("VertID" + (i / 3) + " label - " + pos);
vertId.setLocalTranslation(pos);
vertId.addControl(DebugMaterials.bc.cloneForSpatial(vertId));
node.attachChild(vertId);
}
return node;
}
public static ScenegraphNode buildVoxelGrid(VoxelObjectNode voxelObjectNode) {
ScenegraphNode voxelGrid =
new ScenegraphNode("Voxel grid " + voxelObjectNode.getName());
ScenegraphNode voxelNode = new ScenegraphNode("Voxels");
ScenegraphNode labelNode = new ScenegraphNode("Voxel labels");
float[][][] data = voxelObjectNode.getData();
HashSet<Voxel> voxels = new HashSet<>();
// for (long quad : geometry.getFaceIDs()) {
// // tree = new VoxelQuadTree(quad, geometry.getLOD(),
// // geometry.getZipKey(), geometry.getVoxelData());
// tree = new VoxelQuadTree(quad, geometry, data);
// if ((tree.quadCase > 0) && (tree.quadCase < (QuadCases.caseCount() -
// 1)))
// buildTreeVoxels(tree, voxels);
// }
for (Voxel v : voxels) {
voxelNode.attachChild(v);
labelNode.attachChild(v.valueText);
}
GeometryBatchFactory.optimize(voxelNode);
voxelGrid.attachChild(voxelNode);
voxelGrid.attachChild(labelNode);
return voxelGrid;
}
private static void buildTreeVoxels(VoxelQuadTree tree, HashSet<Voxel> voxels) {
if (tree.isLeaf())
buildLeafVoxels(tree, voxels);
else {
buildTreeVoxels(tree.getBottomLeftTree(), voxels);
buildTreeVoxels(tree.getBottomRightTree(), voxels);
buildTreeVoxels(tree.getTopRightTree(), voxels);
buildTreeVoxels(tree.getTopLeftTree(), voxels);
}
}
private static void buildLeafVoxels(VoxelQuadTree leaf, HashSet<Voxel> voxels) {
float[] data = leaf.getLeafData();
Vector3f pos = new Vector3f(leaf.getX(), leaf.getY(), leaf.getZ());
if ((leaf.quadCase > 0) && (leaf.quadCase < (QuadCases.caseCount() - 1))) {
Voxel voxel = new Voxel(pos.add(0, 0, leaf.size), data[0], leaf.size / 5);
voxels.add(voxel);
voxel = new Voxel(pos.add(leaf.size, 0, leaf.size), data[1], leaf.size / 5);
voxels.add(voxel);
voxel = new Voxel(pos.add(leaf.size, 0, 0), data[2], leaf.size / 5);
voxels.add(voxel);
voxel = new Voxel(pos.add(0, 0, 0), data[3], leaf.size / 5);
voxels.add(voxel);
}
}
}