Files
Gruppe-01-fin/Projekte/jme-common/src/main/java/pp/graphics/Draw.java
2024-09-18 17:04:31 +02:00

230 lines
8.6 KiB
Java

////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.graphics;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Quad;
import pp.util.Position;
import pp.util.SegmentLike;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.HashMap;
import java.util.Map;
import static pp.util.FloatMath.FLT_EPSILON;
import static pp.util.FloatMath.TWO_PI;
import static pp.util.FloatMath.cos;
import static pp.util.FloatMath.sin;
import static pp.util.FloatMath.sqr;
import static pp.util.FloatMath.sqrt;
/**
* Class for creating graphical primitives.
*/
public class Draw {
private static final Logger LOGGER = System.getLogger(Draw.class.getName());
private static final int NUM = 10;
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
private static final String COLOR = "Color"; //NON-NLS
private final AssetManager am;
private final Map<ColorRGBA, Geometry> lineMap = new HashMap<>();
private final Map<ColorRGBA, Geometry> rectangleMap = new HashMap<>();
private final Map<ColorRGBA, Geometry> circleMap = new HashMap<>();
private Mesh lineMesh;
private Mesh circleMesh;
/**
* Creates an in stance of the Draw class with the specified
* asset manager.
*
* @param assetManager the specified asset manager
*/
public Draw(AssetManager assetManager) {
am = assetManager;
}
private Geometry makeLine(ColorRGBA color) {
LOGGER.log(Level.DEBUG, "create line with color {0}", color); //NON-NLS
if (lineMesh == null) {
lineMesh = new Mesh();
lineMesh.setMode(Mesh.Mode.Lines);
lineMesh.setBuffer(VertexBuffer.Type.Position, 3, new float[]{0, 0, 0, 0, 1, 0});
lineMesh.setBuffer(VertexBuffer.Type.Index, 2, new short[]{0, 1});
}
final Geometry lineGeom = new Geometry("lineMesh", lineMesh.clone());
Material matWireframe = new Material(am, UNSHADED); //NON-NLS
matWireframe.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
matWireframe.setColor(COLOR, color); //NON-NLS
lineGeom.setMaterial(matWireframe);
return lineGeom;
}
/**
* Creates a line for the specified segment.
*
* @param segment the segment with its start and end point
* @param z depth information
* @param color line color
*/
public Geometry makeLine(SegmentLike segment, float z, ColorRGBA color) {
return makeLine(segment.from(), segment.to(), z, color);
}
/**
* Creates a straight line between the specified points with the specified color.
*
* @param p1 start point
* @param p2 end point
* @param z depth information
* @param color line color
*/
public Geometry makeLine(Position p1, Position p2, float z, ColorRGBA color) {
return makeLine(p1.getX(), p1.getY(), p2.getX(), p2.getY(), z, color);
}
/**
* Creates a straight line between the specified points with the specified color.
*
* @param x1 x-coordinate of the start point
* @param y1 y-coordinate of the start point
* @param x2 x-coordinate of the end point
* @param y2 y-coordinate of the end point
* @param z depth information
* @param color line color
*/
public Geometry makeLine(float x1, float y1, float x2, float y2, float z, ColorRGBA color) {
final Geometry line = lineMap.computeIfAbsent(color, this::makeLine).clone();
line.lookAt(Vector3f.UNIT_Z, new Vector3f(x2 - x1, y2 - y1, 0));
line.setLocalScale(sqrt(sqr(x2 - x1) + sqr(y2 - y1)));
line.setLocalTranslation(x1, y1, z);
return line;
}
/**
* Creates a straight line between the specified points with the specified width and color.
*
* @param p1 start point
* @param p2 end point
* @param z depth information
* @param color line color
* @param width width of the line
*/
public Geometry makeFatLine(Position p1, Position p2, float z, ColorRGBA color, float width) {
return makeFatLine(p1.getX(), p1.getY(), p2.getX(), p2.getY(), z, color, width);
}
/**
* Creates a straight line between the specified points with the specified width and color.
*
* @param x1 x-coordinate of the start point
* @param y1 y-coordinate of the start point
* @param x2 x-coordinate of the end point
* @param y2 y-coordinate of the end point
* @param z depth information
* @param color line color
* @param width width of the line
*/
public Geometry makeFatLine(float x1, float y1, float x2, float y2, float z, ColorRGBA color, float width) {
final Geometry line = rectangleMap.computeIfAbsent(color, this::makeRectangle).clone();
final float dx = x2 - x1;
final float dy = y2 - y1;
final float len = sqrt(dx * dx + dy * dy);
line.setLocalScale(width, len + width, 1f);
if (len <= FLT_EPSILON)
line.setLocalTranslation(x1 - 0.5f * width, y1 - 0.5f * width, z);
else {
final float f = 0.5f * width / len;
line.setLocalTranslation(x1 - f * (dy + dx), y1 - f * (dy - dx), z);
line.getLocalRotation().lookAt(Vector3f.UNIT_Z, new Vector3f(dx, dy, 0f));
}
return line;
}
private Geometry makeRectangle(ColorRGBA color) {
final Mesh quad = new Quad(1f, 1f);
final Geometry rectangle = new Geometry("quad", quad); //NON-NLS
Material mat = new Material(am, UNSHADED); //NON-NLS
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
mat.setColor(COLOR, color); //NON-NLS
rectangle.setMaterial(mat);
return rectangle;
}
/**
* Creates an axis-parallel rectangle with the specified color.
*
* @param x x-coordinate of the bottom-left corner
* @param y y-coordinate of the bottom-left corner
* @param w width of the rectangle
* @param h height of the rectangle
* @param z depth information
* @param color line color
*/
public Geometry makeRectangle(float x, float y, float z, float w, float h, ColorRGBA color) {
final Geometry rectangle = rectangleMap.computeIfAbsent(color, this::makeRectangle).clone();
rectangle.setLocalScale(w, h, 1f);
rectangle.setLocalTranslation(x, y, z);
return rectangle;
}
private Geometry makeCircle(ColorRGBA color) {
if (circleMesh == null) {
circleMesh = new Mesh();
circleMesh.setMode(Mesh.Mode.LineLoop);
final float[] pointBuffer = new float[3 * NUM];
final short[] indexBuffer = new short[NUM];
int j = 0;
for (short i = 0; i < NUM; i++) {
final float a = TWO_PI / NUM * i;
pointBuffer[j++] = 0.5f * cos(a);
pointBuffer[j++] = 0.5f * sin(a);
pointBuffer[j++] = 0f;
indexBuffer[i] = i;
}
circleMesh.setBuffer(VertexBuffer.Type.Position, 3, pointBuffer);
circleMesh.setBuffer(VertexBuffer.Type.Index, 2, indexBuffer);
}
final Geometry circle = new Geometry("circleMesh", circleMesh.clone());
Material matWireframe = new Material(am, UNSHADED); //NON-NLS
matWireframe.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
matWireframe.setColor(COLOR, color); //NON-NLS
circle.setMaterial(matWireframe);
return circle;
}
/**
* Creates an ellipse with the specified color.
*
* @param x x-coordinate of the center point
* @param y y-coordinate of the center point
* @param w width of the ellipse
* @param h height of the ellipse
* @param z depth information
* @param color line color
*/
public Geometry makeEllipse(float x, float y, float z, float w, float h, ColorRGBA color) {
final Geometry ellipse = circleMap.computeIfAbsent(color, this::makeCircle).clone();
ellipse.setLocalScale(w, h, 1f);
ellipse.setLocalTranslation(x, y, z);
return ellipse;
}
}