Allow rotating text with native painter

This commit is contained in:
Martin Pernollet 2022-01-26 10:25:58 +01:00
parent 80a3e89cf6
commit aeabc19238
5 changed files with 408 additions and 274 deletions

View File

@ -7,6 +7,9 @@ import com.diogonunes.jcolor.Attribute;
/**
* A console output helper able to add coloring in console.
*
* It require the console to have an ANSI interpreter, e.g. Eclipse needs something like
* <a href="https://marketplace.eclipse.org/content/ansi-escape-console">this</a>.
*
* @author Martin Pernollet
*
*/
@ -28,6 +31,7 @@ public class Console {
}
public static Attribute toBackground(Color background) {
return Attribute.BACK_COLOR((int)(background.r*255), (int)(background.g*255), (int)(background.b*255));
return Attribute.BACK_COLOR((int) (background.r * 255), (int) (background.g * 255),
(int) (background.b * 255));
}
}

View File

@ -708,6 +708,13 @@ public class EmulGLPainter extends AbstractPainter implements IPainter {
color.b, 0);
}
/**
* Render 2D text at the given 3D position.
*
* The {@link Font} can be any font name and size supported by AWT.
*
* Rotation is in radian and is applied at the center of the text to avoid messing up text layout.
*/
@Override
public void drawText(Font font, String label, Coord3d position, Color color, float rotation) {
glut.glutBitmapString(toAWT(font), label, position.x, position.y, position.z, color.r, color.g,

View File

@ -30,292 +30,293 @@ import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.MemoryImageSource;
import java.awt.image.RenderedImage;
import jgl.ImageToDraw;
import jgl.TextToDraw;
import jgl.context.gl_util;
public final class GL extends jgl.GL<BufferedImage, Font> {
protected Component canvas;
protected BufferedImage glImage;
protected boolean renderedOnce = false;
protected Component canvas;
protected BufferedImage glImage;
protected boolean renderedOnce = false;
/**
* Draws the image buffer that was built by {@link GL#glFlush()} with the caller {@link Graphics}
* context
*
* OpenGL equivalent:
*
* <code>void glXSwapBuffers (Display *dpy, GLXDrawable drawable)</code>
*/
public void glXSwapBuffers(Graphics g, ImageObserver o) {
/**
* Draws the image buffer that was built by {@link GL#glFlush()} with the caller {@link Graphics}
* context
*
* OpenGL equivalent:
*
* <code>void glXSwapBuffers (Display *dpy, GLXDrawable drawable)</code>
*/
public void glXSwapBuffers(Graphics g, ImageObserver o) {
g.drawImage(glImage, StartX, StartY, desiredWidth, desiredHeight, o);
}
g.drawImage(glImage, StartX, StartY, desiredWidth, desiredHeight, o);
}
public void glXSwapBuffers(Graphics g, Applet o) {
glXSwapBuffers(g, (ImageObserver) o);
}
public void glXSwapBuffers(Graphics g, Applet o) {
@Override
public void glFlush() {
glXSwapBuffers(g, (ImageObserver)o);
}
if (Context.RenderMode != GL_RENDER) {
return;
}
// DEBUG
// checkColorBuffer();
@Override
public void glFlush() {
// ------------------------------------------
// Create an image producer based on
// colorbuffer into which GL draws
MemoryImageSource producer = new MemoryImageSource(Context.Viewport.Width,
Context.Viewport.Height, Context.ColorBuffer.Buffer, 0, Context.Viewport.Width);
// producer.setAnimated(true);
// producer.setFullBufferUpdates(true);
// Generates an image from the toolkit to use this producer
if(Context.RenderMode != GL_RENDER) {
return;
}
// DEBUG
// checkColorBuffer();
// ------------------------------------------
// Create an image producer based on
// colorbuffer into which GL draws
MemoryImageSource producer = new MemoryImageSource(Context.Viewport.Width, Context.Viewport.Height, Context.ColorBuffer.Buffer, 0, Context.Viewport.Width);
// producer.setAnimated(true);
// producer.setFullBufferUpdates(true);
// Generates an image from the toolkit to use this producer
Image jGLColorBuffer = canvas.createImage(producer);
// ------------------------------------------
// Write GL content in a temporary image
// and then to the image returned to Canvas
glImage = new BufferedImage(jGLColorBuffer.getWidth(null), jGLColorBuffer.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D)glImage.getGraphics();
configureRenderingHints(g2d);
// Hack background
if(clearBackgroundWithG2d)
hackClearColorWithG2DfillRect(g2d);
// Text that should appear BEHIND the scene's polygons
drawTexts(g2d);
// Images that should appear BEHIND the scene's polygons
// drawImagesAndClearBuffer(g2d);
drawImages(g2d, ImageLayer.BACKGROUND);
// Color buffer
g2d.drawImage(jGLColorBuffer, shiftHorizontally, 0, null);
// Text that should appear ON TOP of the scene's polygons
// ...
// Images that should appear ON TOP of the scene's polygons
drawImages(g2d, ImageLayer.FOREGROUND);
clearImagesBuffer();
// debugWriteImageTo("target/jGL.glFlush.png", (RenderedImage)JavaImage);
}
Image jGLColorBuffer = canvas.createImage(producer);
public BufferedImage getRenderedImage() {
// ------------------------------------------
// Write GL content in a temporary image
// and then to the image returned to Canvas
glImage = new BufferedImage(jGLColorBuffer.getWidth(null), jGLColorBuffer.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) glImage.getGraphics();
configureRenderingHints(g2d);
return glImage;
}
// Hack background
if (clearBackgroundWithG2d)
hackClearColorWithG2DfillRect(g2d);
@Override
public void applyViewport() {
// Text that should appear BEHIND the scene's polygons
drawTexts(g2d);
// Update pixel scale to guess if HiDPI
if(canvas != null && canvas.getGraphics() != null)
updatePixelScale(canvas.getGraphics());
super.applyViewport();
}
// Images that should appear BEHIND the scene's polygons
// drawImagesAndClearBuffer(g2d);
drawImages(g2d, ImageLayer.BACKGROUND);
/** Because this is for Java, use true color and double buffer default */
/** Bool glXMakeCurrent (Display *dpy, GLXDrawable drawable, GLXcontext ctx) */
// public boolean glXMakeCurrent (Applet o, int x, int y) {
public boolean glXMakeCurrent(Component o, int x, int y) {
// Color buffer
g2d.drawImage(jGLColorBuffer, shiftHorizontally, 0, null);
// Text that should appear ON TOP of the scene's polygons
// ...
// JavaApplet = o;
canvas = o;
StartX = x;
StartY = y;
// Context.gl_initialize_context (o.getSize().width, o.getSize().height);
glViewport(x, y, o.getSize().width, o.getSize().height);
Context.gl_initialize_context();
return GL_TRUE;
}
// Images that should appear ON TOP of the scene's polygons
drawImages(g2d, ImageLayer.FOREGROUND);
clearImagesBuffer();
// debugWriteImageTo("target/jGL.glFlush.png", (RenderedImage)JavaImage);
}
public Component glJGetComponent() {
public BufferedImage getRenderedImage() {
return canvas;
}
return glImage;
}
public void updatePixelScale(Graphics g) {
@Override
public void applyViewport() {
Graphics2D g2d = (Graphics2D)g;
// printGlobalScale(g2d);
// produce 2.0 factory on MacOS with Retina
// produce 1.5 factor on Win10 HiDPI on the same Apple hardware as above
// We will read pixel scale from G2D while swapping images. This means
// we may be late of 1 image to adapt to an HiDPI change.
if(autoAdaptToHiDPI) {
getPixelScaleFromG2D(g2d);
if(!renderedOnce) {
// no event was sent has the default values
// are 1,1 so we force an event
if(pixelScaleX == 1 && pixelScaleY == 1)
firePixelScaleChanged(1, 1);
renderedOnce = true;
}
} else {
resetPixelScale();
if(!renderedOnce) {
firePixelScaleChanged(1, 1);
renderedOnce = true;
}
}
}
// Update pixel scale to guess if HiDPI
if (canvas != null && canvas.getGraphics() != null)
updatePixelScale(canvas.getGraphics());
super.applyViewport();
}
/** Pixel scale is used to model the pixel ratio introduced by HiDPI */
protected void getPixelScaleFromG2D(Graphics2D g2d) {
/** Because this is for Java, use true color and double buffer default */
/** Bool glXMakeCurrent (Display *dpy, GLXDrawable drawable, GLXcontext ctx) */
// public boolean glXMakeCurrent (Applet o, int x, int y) {
public boolean glXMakeCurrent(Component o, int x, int y) {
canvas = o;
StartX = x;
StartY = y;
AffineTransform globalTransform = g2d.getTransform();
double oldPixelScaleX = pixelScaleX;
double oldPixelScaleY = pixelScaleY;
double newPixelScaleX = globalTransform.getScaleX();
double newPixelScaleY = globalTransform.getScaleY();
setPixelScaleX(newPixelScaleX);
setPixelScaleY(newPixelScaleY);
if(newPixelScaleX != oldPixelScaleX || newPixelScaleY != oldPixelScaleY) {
firePixelScaleChanged(newPixelScaleX, newPixelScaleY);
}
}
glViewport(x, y, o.getSize().width, o.getSize().height);
Context.gl_initialize_context();
return GL_TRUE;
}
protected void printGlobalScale(Graphics2D g2d) {
public Component glJGetComponent() {
return canvas;
}
AffineTransform globalTransform = g2d.getTransform();
double globalScaleX = globalTransform.getScaleX();
double globalScaleY = globalTransform.getScaleY();
System.out.println("globalScaleX:" + globalScaleX + " globalScaleY:" + globalScaleY);
}
public void updatePixelScale(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
// printGlobalScale(g2d);
// produce 2.0 factory on MacOS with Retina
// produce 1.5 factor on Win10 HiDPI on the same Apple hardware as above
// We will read pixel scale from G2D while swapping images. This means
// we may be late of 1 image to adapt to an HiDPI change.
if (autoAdaptToHiDPI) {
getPixelScaleFromG2D(g2d);
if (!renderedOnce) {
// no event was sent has the default values
// are 1,1 so we force an event
if (pixelScaleX == 1 && pixelScaleY == 1)
firePixelScaleChanged(1, 1);
renderedOnce = true;
}
} else {
resetPixelScale();
if (!renderedOnce) {
firePixelScaleChanged(1, 1);
renderedOnce = true;
}
}
}
protected void configureRenderingHints(Graphics2D g2d) {
/** Pixel scale is used to model the pixel ratio introduced by HiDPI */
protected void getPixelScaleFromG2D(Graphics2D g2d) {
AffineTransform globalTransform = g2d.getTransform();
double oldPixelScaleX = pixelScaleX;
double oldPixelScaleY = pixelScaleY;
double newPixelScaleX = globalTransform.getScaleX();
double newPixelScaleY = globalTransform.getScaleY();
setPixelScaleX(newPixelScaleX);
setPixelScaleY(newPixelScaleY);
if (newPixelScaleX != oldPixelScaleX || newPixelScaleY != oldPixelScaleY) {
firePixelScaleChanged(newPixelScaleX, newPixelScaleY);
}
}
RenderingHints rh = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHints(rh);
}
protected void printGlobalScale(Graphics2D g2d) {
AffineTransform globalTransform = g2d.getTransform();
double globalScaleX = globalTransform.getScaleX();
double globalScaleY = globalTransform.getScaleY();
System.out.println("globalScaleX:" + globalScaleX + " globalScaleY:" + globalScaleY);
}
protected void hackClearColorWithG2DfillRect(Graphics2D g2d) {
protected void configureRenderingHints(Graphics2D g2d) {
RenderingHints rh = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHints(rh);
}
Color backgroundColor = getClearColorAWT();
g2d.setColor(backgroundColor);
g2d.fillRect(0, 0, Context.Viewport.Width, Context.Viewport.Height);
}
protected void hackClearColorWithG2DfillRect(Graphics2D g2d) {
Color backgroundColor = getClearColorAWT();
g2d.setColor(backgroundColor);
g2d.fillRect(0, 0, Context.Viewport.Width, Context.Viewport.Height);
}
/**
* Convert a color given as an integer to an AWT Color.
*/
public static Color glIntToColor(int color) {
/**
* Convert a color given as an integer to an AWT Color.
*/
public static Color glIntToColor(int color) {
float r = gl_util.ItoR(color);
float g = gl_util.ItoG(color);
float b = gl_util.ItoB(color);
// float a = gl_util.ItoA(color);
return new Color(r / 255, g / 255, b / 255);
}
float r = gl_util.ItoR(color);
float g = gl_util.ItoG(color);
float b = gl_util.ItoB(color);
// float a = gl_util.ItoA(color);
return new Color(r / 255, g / 255, b / 255);
}
/**
* @return the clear color as an AWT Color.
*/
public Color getClearColorAWT() {
int clearColor = getContext().ColorBuffer.IntClearColor;
return glIntToColor(clearColor);
}
/**
* @return the clear color as an AWT Color.
*/
public Color getClearColorAWT() {
int clearColor = getContext().ColorBuffer.IntClearColor;
return glIntToColor(clearColor);
}
protected void drawImagesAndClearBuffer(Graphics2D g2d) {
protected void drawImagesAndClearBuffer(Graphics2D g2d) {
synchronized (imageToDraw) {
for (ImageToDraw<BufferedImage> img : imageToDraw) {
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
}
imageToDraw.clear(); // empty image buffer
}
}
synchronized(imageToDraw) {
for(ImageToDraw<BufferedImage> img : imageToDraw) {
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
}
imageToDraw.clear(); // empty image buffer
}
}
protected void drawImages(Graphics2D g2d, ImageLayer layer) {
synchronized (imageToDraw) {
for (ImageToDraw<BufferedImage> img : imageToDraw) {
if (img.layer == null || img.layer.equals(layer)) {
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
}
}
}
}
protected void drawImages(Graphics2D g2d, ImageLayer layer) {
/**
* Renders appended text to given {@link Graphics2D} context.
*
* @param g2d
*/
protected void drawTexts(Graphics2D g2d) {
synchronized (textsToDraw) {
for (TextToDraw<Font> text : textsToDraw) {
doDrawString(g2d, text);
}
textsToDraw.clear(); // empty text buffer
}
}
synchronized(imageToDraw) {
for(ImageToDraw<BufferedImage> img : imageToDraw) {
if(img.layer == null || img.layer.equals(layer)) {
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
}
}
}
}
protected void doDrawString(Graphics2D g2d, TextToDraw<Font> text) {
// Coloring
if (text.r >= 0) {
g2d.setColor(new Color(text.r, text.g, text.b));
} else {
g2d.setColor(Color.BLACK);
}
// Position
int x = text.x + shiftHorizontally;
int y = text.y;
// Get text width
int textWidth = 0;
g2d.setFont(text.font);
FontMetrics fm = g2d.getFontMetrics();
if (fm != null) {
textWidth = fm.stringWidth(text.string);
}
// Configure context for rotation
preRotateFromLeftPoint(g2d, x, y, text.rotate, textWidth);
// rotation point / position of text
// g2d.fillRect(-1, -1, 2, 2);
// Render text
if (useOSFontRendering) {
g2d.drawString(text.string, 0, 0);
} else {
FontRenderContext frc = g2d.getFontRenderContext();
GlyphVector gv = text.font.createGlyphVector(frc, text.string);
g2d.drawGlyphVector(gv, 0, 0);
}
// Reset rotation from context
postRotateFromLeftPoint(g2d, x, y, text.rotate, textWidth);
}
/**
* Renders appended text to given {@link Graphics2D} context.
*
* @param g2d
*/
protected void drawTexts(Graphics2D g2d) {
/**
* Configure graphics to rotate text and shift the text horizontally
*/
protected void preRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate, int textWidth) {
synchronized(textsToDraw) {
for(TextToDraw<Font> text : textsToDraw) {
doDrawString(g2d, text);
}
textsToDraw.clear(); // empty text buffer
}
}
if (rotate != 0) {
g2d.translate(textWidth / 2, 0);
}
g2d.translate(x, y);
if (rotate != 0) {
g2d.rotate(rotate);
g2d.translate(-textWidth / 2, 0);
}
}
protected void doDrawString(Graphics2D g2d, TextToDraw<Font> text) {
/**
* Apply reverse rotation and translation to reset graphics context.
*/
protected void postRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate,
int textWidth) {
if (rotate != 0) {
g2d.translate(textWidth / 2, 0);
g2d.rotate(-rotate);
}
g2d.translate(-x, -y);
if (rotate != 0) {
g2d.translate(-textWidth / 2, 0);
}
}
// AffineTransform orig = g2d.getTransform();
// g2d.rotate(Math.PI/2);
if(text.r >= 0) {
g2d.setColor(new Color(text.r, text.g, text.b));
} else {
g2d.setColor(Color.BLACK);
}
int x = text.x + shiftHorizontally;
int y = text.y;
float rotate = text.rotate;
int textWidth = 0;
g2d.setFont(text.font);
FontMetrics fm = g2d.getFontMetrics();
if(fm != null) {
textWidth = fm.stringWidth(text.string);
}
preRotateFromLeftPoint(g2d, x, y, rotate, textWidth);
// rotation point / position of text
// g2d.fillRect(-1, -1, 2, 2);
if(useOSFontRendering) {
g2d.drawString(text.string, 0, 0);
} else {
FontRenderContext frc = g2d.getFontRenderContext();
GlyphVector gv = text.font.createGlyphVector(frc, text.string);
g2d.drawGlyphVector(gv, 0, 0);
}
postRotateFromLeftPoint(g2d, x, y, rotate, textWidth);
}
protected void preRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate, int textWidth) {
if(rotate != 0) {
g2d.translate(textWidth / 2, 0);
}
g2d.translate(x, y);
if(rotate != 0) {
g2d.rotate(rotate);
g2d.translate(-textWidth / 2, 0);
}
}
protected void postRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate, int textWidth) {
if(rotate != 0) {
g2d.translate(textWidth / 2, 0);
g2d.rotate(-rotate);
}
g2d.translate(-x, -y);
if(rotate != 0) {
g2d.translate(-textWidth / 2, 0);
}
}
}
}

View File

@ -8,6 +8,7 @@ import org.jzy3d.maths.Coord3d;
import org.jzy3d.painters.AWTFont;
import org.jzy3d.painters.Font;
import org.jzy3d.painters.IPainter;
import org.jzy3d.painters.NativeDesktopPainter;
import org.jzy3d.plot3d.primitives.PolygonFill;
import org.jzy3d.plot3d.primitives.PolygonMode;
import org.jzy3d.plot3d.text.AbstractTextRenderer;
@ -16,6 +17,7 @@ import org.jzy3d.plot3d.text.align.AWTTextLayout;
import org.jzy3d.plot3d.text.align.Horizontal;
import org.jzy3d.plot3d.text.align.Vertical;
import org.jzy3d.plot3d.text.renderers.jogl.style.DefaultTextStyle;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.awt.TextRenderer.RenderDelegate;
@ -72,14 +74,14 @@ public class JOGLTextRenderer2d extends AbstractTextRenderer implements ITextRen
// Reset to a polygon mode suitable for rendering the texture handling the text
painter.glPolygonMode(PolygonMode.FRONT_AND_BACK, PolygonFill.FILL);
drawText2D(painter, font, s, position, color, horizontal, vertical);
drawText2D(painter, font, s, position, color, rotation, horizontal, vertical);
return null;
}
/** Draws a 2D text (facing camera) at the specified 3D position. */
protected void drawText2D(IPainter painter, Font font, String text, Coord3d position,
Color color, Horizontal horizontal, Vertical vertical) {
Color color, float rotation, Horizontal horizontal, Vertical vertical) {
// Canvas size
int width = painter.getView().getCanvas().getRendererWidth();
@ -90,14 +92,42 @@ public class JOGLTextRenderer2d extends AbstractTextRenderer implements ITextRen
Coord2d textSize = layout.getBounds(text, awtFont, renderer.getFontRenderContext());
screen = layout.align(textSize.x, textSize.y, horizontal, vertical, screen);
GL2 gl = ((NativeDesktopPainter)painter).getGL().getGL2();
// Render text
renderer.setColor(color.r, color.g, color.b, color.a);
renderer.beginRendering(width, height);
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glPushMatrix();
renderer.draw(text, (int) screen.x, (int) screen.y);
renderer.flush();
rotation = (float)(360*rotation / (2*Math.PI));
//rotation = 90;
gl.glTranslatef(screen.x, screen.y, 0);
gl.glRotatef(rotation, 0, 0, 1);
//System.out.println(rotation);
int x =0, y = 0;
/*if(Math.abs(rotation)==90) {
x = (int)(textSize.y / 2);
y = (int)(textSize.x / 2);
}*/
renderer.draw(text, x, y);
//renderer.draw(text, (int) screen.x, (int) screen.y);
renderer.endRendering();
renderer.flush();
gl.glPopMatrix();
}

View File

@ -7,6 +7,8 @@ import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jzy3d.colors.Color;
@ -30,11 +32,12 @@ import com.jogamp.opengl.fixedfunc.GLLightingFunc;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.glu.GLUquadric;
import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.gl2.GLUT;
public class NativeDesktopPainter extends AbstractPainter implements IPainter {
static Logger LOGGER = LogManager.getLogger(NativeDesktopPainter.class);
protected GL gl;
protected GLU glu = new GLU();
protected GLUT glut = new GLUT();
@ -43,6 +46,10 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
return gl;
}
public GL2 getGL2() {
return getGL().getGL2();
}
public void setGL(GL gl) {
this.gl = gl;
}
@ -141,23 +148,22 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
} else {
gl.glDisable(GL2.GL_ALPHA_TEST);
}
// Make smooth colors for polygons (interpolate color between points)
if(gl.getGLProfile().isGL2()) {
if (quality.isSmoothColor())
if (gl.getGLProfile().isGL2()) {
if (quality.isSmoothColor())
gl.getGL2().glShadeModel(GLLightingFunc.GL_SMOOTH);
else
gl.getGL2().glShadeModel(GLLightingFunc.GL_FLAT);
} else {
LOGGER.warn(
"Did not configured shade model as we don t have a GL2 context : " + gl.getGLProfile());
}
else {
LOGGER.warn("Did not configured shade model as we don t have a GL2 context : " + gl.getGLProfile());
}
/*else if(gl.getGLProfile().isGL4()) {
if (quality.isSmoothColor())
gl.getGL4().glShadeModel(GLLightingFunc.GL_SMOOTH);
else
gl.getGL2().glShadeModel(GLLightingFunc.GL_FLAT);
}*/
/*
* else if(gl.getGLProfile().isGL4()) { if (quality.isSmoothColor())
* gl.getGL4().glShadeModel(GLLightingFunc.GL_SMOOTH); else
* gl.getGL2().glShadeModel(GLLightingFunc.GL_FLAT); }
*/
// Make smoothing setting
if (quality.isSmoothPolygon()) {
@ -448,11 +454,97 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
glut.glutBitmapString(font, string);
}
/**
* Render 2D text at the given 3D position.
*
* The {@link Font} can be any font name and size supported by AWT.
*
* Rotation is in radian and is applied at the center of the text to avoid messing up text layout.
*
* @see {@link #glutBitmapString(int, String)}, an alternative way of rendering text with simpler
* parameters and smaller font name and size set.
*/
@Override
public void drawText(Font font, String label, Coord3d position, Color color, float rotation) {
glutBitmapString(font, label, position, color);
// OLD WAY
//glutBitmapString(font, label, position, color);
// Reset to a polygon mode suitable for rendering the texture handling the text
glPolygonMode(PolygonMode.FRONT_AND_BACK, PolygonFill.FILL);
// Geometric processing for text layout
int width = getCanvas().getRendererWidth();
int height = getCanvas().getRendererHeight();
float rotationD = -(float) (360 * rotation / (2 * Math.PI));
Coord3d screen = modelToScreen(position);
TextRenderer renderer = getOrCreateTextRenderer(font);
renderer.setColor(color.r, color.g, color.b, color.a);
renderer.beginRendering(width, height);
// System.out.println(label + "\t" + screen);
// Console.print(label + "\t" + screen, color);
int xPreShift = 0, yPreShift = 0;
if (rotation != 0) {
// xPreShift = (int)(Math.cos(rotation)*getTextLengthInPixels(font, label));
// yPreShift = (int)(Math.sin(rotation)*font.getHeight());
xPreShift = getTextLengthInPixels(font, label) / 2;
yPreShift = 0;
}
GL2 gl = getGL2();
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glPushMatrix();
gl.glTranslatef(screen.x + xPreShift, screen.y + yPreShift, 0);
gl.glRotatef(rotationD, 0, 0, 1);
// Shifting text to deal with rotation
int xPostShift = 0, yPostShift = 0;
if (rotation != 0) {
xPostShift = (int) (Math.cos(rotation) * getTextLengthInPixels(font, label));
yPostShift = (int) (Math.sin(rotation) * font.getHeight());
xPostShift = -xPreShift;// getTextLengthInPixels(font, label)/2;
yPostShift = -yPreShift;
}
renderer.draw(label, xPostShift, yPostShift);
renderer.endRendering();
renderer.flush();
gl.glPopMatrix();
}
protected TextRenderer getOrCreateTextRenderer(Font font) {
TextRenderer renderer = txtRendererMap.get(font);
if (renderer == null) {
renderer = new TextRenderer(toAWT(font), true, true, null);
// renderer.setSmoothing(false);// some GPU do not handle smoothing well
// renderer.setUseVertexArrays(false); // some GPU do not handle VBO properly
txtRendererMap.put(font, renderer);
}
return renderer;
}
protected Map<Font, TextRenderer> txtRendererMap = new HashMap<>();
// protected TextLayout layout = new TextLayout();
/**
* Render 2D text at the given 3D position using a font as supported by
* {@link #glutBitmapString(int, String)}.
*
* @see {@link Font} to know the set of font name and size supported by this method.
*/
@Override
public void glutBitmapString(Font axisFont, String label, Coord3d p, Color c) {
color(c);
@ -655,11 +747,11 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
}
@Override
public void glStencilMask_False(){
gl.glStencilMask(GL.GL_FALSE);
public void glStencilMask_False() {
gl.glStencilMask(GL.GL_FALSE);
}
@Override
public void glStencilOp(StencilOp fail, StencilOp zfail, StencilOp zpass) {
gl.glStencilOp(toInt(fail), toInt(zfail), toInt(zpass));
@ -722,7 +814,7 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
@Override
public void glClipPlane(int plane, double[] equation) {
//Array.print("NativePainter : glClipPlane : " + plane + " : ", equation);
// Array.print("NativePainter : glClipPlane : " + plane + " : ", equation);
gl.getGL2().glClipPlane(plane, equation, 0);
}
@ -730,8 +822,8 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
public void glEnable_ClipPlane(int plane) {
gl.glEnable(clipPlaneId(plane));
}
/** Return the GL clip plane ID according to an ID in [0;5]*/
/** Return the GL clip plane ID according to an ID in [0;5] */
@Override
public int clipPlaneId(int id) {
switch (id) {
@ -1250,12 +1342,12 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
public void glDisable_DepthTest() {
gl.glDisable(GL.GL_DEPTH_TEST);
}
@Override
public void glEnable_Stencil() {
gl.glEnable(GL2.GL_STENCIL_TEST);
}
@Override
public void glDisable_Stencil() {
gl.glDisable(GL2.GL_STENCIL_TEST);