Integrating text rendering with the new immediate mode

This commit is contained in:
codeanticode
2011-12-05 05:33:40 +00:00
parent 60377b0c13
commit f78e17413b
3 changed files with 416 additions and 80 deletions
@@ -27,6 +27,7 @@ import java.util.HashMap;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PFont;
import processing.core.PImage;
/**
* All the infrastructure needed for optimized font rendering
@@ -56,6 +57,7 @@ class PFontTexture implements PConstants {
protected int offsetY;
protected int lineHeight;
protected PTexture[] textures = null;
protected PImage[] images = null;
protected int currentTex;
protected int lastTex;
protected TextureInfo[] glyphTexinfos;
@@ -143,6 +145,10 @@ class PFontTexture implements PConstants {
if (textures == null) {
textures = new PTexture[1];
textures[0] = tex;
images = new PImage[1];
images[0] = new PImage(tex.width, tex.height, ARGB);
images[0].setCache(ogl, tex);
currentTex = 0;
} else if (resize) {
// Replacing old smaller texture with larger one.
@@ -151,6 +157,10 @@ class PFontTexture implements PConstants {
PTexture tex0 = textures[currentTex];
tex.put(tex0);
textures[currentTex] = tex;
images[currentTex].setCache(ogl, tex);
images[currentTex].width = tex.width;
images[currentTex].height = tex.height;
} else {
// Adding new texture to the list.
PTexture[] temp = textures;
@@ -158,6 +168,13 @@ class PFontTexture implements PConstants {
PApplet.arrayCopy(temp, textures, temp.length);
textures[temp.length] = tex;
currentTex = textures.length - 1;
PImage[] temp2 = images;
images = new PImage[textures.length + 1];
PApplet.arrayCopy(temp2, images, temp2.length);
images[temp.length] = new PImage(tex.width, tex.height, ARGB);
images[temp.length].setCache(ogl, tex);
}
lastTex = currentTex;
@@ -181,6 +198,15 @@ class PFontTexture implements PConstants {
}
public PImage getTexture(int idx) {
if (0 <= idx && idx < images.length) {
currentTex = idx;
return images[currentTex];
}
return null;
}
// Add all the current glyphs to opengl texture.
public void addAllGlyphsToTexture() {
// loop over current glyphs.
@@ -2786,13 +2786,14 @@ public class PGraphicsOpenGL extends PGraphics {
*/
protected void textLineImpl(char buffer[], int start, int stop, float x, float y) {
// Init opengl state for text rendering...
gl.glEnable(GL.GL_TEXTURE_2D);
// gl.glEnable(GL.GL_TEXTURE_2D);
//
// if (screenBlendMode != BLEND) {
// gl.glEnable(GL.GL_BLEND);
// if (blendEqSupported) gl.glBlendEquation(GL.GL_FUNC_ADD);
// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
// }
if (screenBlendMode != BLEND) {
gl.glEnable(GL.GL_BLEND);
if (blendEqSupported) gl.glBlendEquation(GL.GL_FUNC_ADD);
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
}
textTex = (PFontTexture)textFont.getCache(ogl);
if (textTex == null) {
@@ -2801,43 +2802,43 @@ public class PGraphicsOpenGL extends PGraphics {
}
textTex.setFirstTexture();
// Setting the current fill color as the font color.
setFillColor();
if (textMode == MODEL) {
if (textVertexBuffer == null) {
allocateTextModel();
}
// Setting Z axis as the normal to the text geometry
setDefNormals(0, 0, 1);
if (!textBlockMode) {
// Resetting vertex count when we are not defining a
// block of text.
textVertexCount = 0;
}
}
// // Setting the current fill color as the font color.
// setFillColor();
//
// if (textMode == MODEL) {
// if (textVertexBuffer == null) {
// allocateTextModel();
// }
//
// // Setting Z axis as the normal to the text geometry
// setDefNormals(0, 0, 1);
//
// if (!textBlockMode) {
// // Resetting vertex count when we are not defining a
// // block of text.
// textVertexCount = 0;
// }
// }
super.textLineImpl(buffer, start, stop, x, y);
if (textMode == MODEL && 0 < textVertexCount) {
if (!textBlockMode) {
// Pushing text geometry to the GPU.
renderTextModel();
} else {
// We don't push any geometry here because we will
// do it when endText is called. For now we just
// save the current texture.
textBlockTex = textTex.currentTex;
}
}
// Restoring current blend mode.
blendMode(screenBlendMode);
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
gl.glDisable(GL.GL_TEXTURE_2D);
// if (textMode == MODEL && 0 < textVertexCount) {
// if (!textBlockMode) {
// // Pushing text geometry to the GPU.
// renderTextModel();
// } else {
// // We don't push any geometry here because we will
// // do it when endText is called. For now we just
// // save the current texture.
// textBlockTex = textTex.currentTex;
// }
// }
//
// // Restoring current blend mode.
// blendMode(screenBlendMode);
//
// gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
// gl.glDisable(GL.GL_TEXTURE_2D);
}
protected void textCharImpl(char ch, float x, float y) {
@@ -2867,25 +2868,24 @@ public class PGraphicsOpenGL extends PGraphics {
}
}
protected void textCharModelImpl(PFontTexture.TextureInfo info, float x1, float y1,
float x2, float y2) {
if ((textTex.currentTex != info.texIndex) ||
(textBlockMode && textBlockTex != info.texIndex)) {
if (0 < textVertexCount) {
// Current texture changes (the font is so large that needs more than one texture).
// So rendering all we got until now, and reseting vertex counter.
renderTextModel();
textVertexCount = 0;
}
textTex.setTexture(info.texIndex);
}
// Division by three needed because each int element in the buffer is used
// to store three coordinates.
if (textVertexBuffer.capacity() / 3 < textVertexCount + 6) {
expandTextBuffers();
}
protected void textCharModelImpl(PFontTexture.TextureInfo info, float x0, float y0,
float x1, float y1) {
PImage tex = textTex.getTexture(info.texIndex);
int mode0 = textureMode;
noStroke();
textureMode(NORMAL);
beginShape(QUADS);
texture(tex);
normal(0, 0, 1);
vertex(x0, y0, info.u0, info.v0);
vertex(x1, y0, info.u1, info.v0);
vertex(x1, y1, info.u1, info.v1);
vertex(x0, y1, info.u0, info.v1);
endShape();
textureMode(mode0);
/*
int n = textVertexCount;
textVertexArray[3 * n + 0] = x1;
textVertexArray[3 * n + 1] = y1;
@@ -2928,8 +2928,7 @@ public class PGraphicsOpenGL extends PGraphics {
textTexCoordArray[2 * n + 0] = info.u1;
textTexCoordArray[2 * n + 1] = info.v1;
n++;
textVertexCount = n;
*/
}
protected void textCharScreenImpl(PFontTexture.TextureInfo info, int xx, int yy,
@@ -32,6 +32,7 @@ import processing.core.PGraphics;
import processing.core.PImage;
import processing.core.PMatrix3D;
import processing.core.PShape;
import processing.core.PStyle;
import processing.opengl.PGraphicsOpenGL.InGeometry;
import processing.opengl.PGraphicsOpenGL.TessGeometry;
import processing.opengl.PGraphicsOpenGL.Tessellator;
@@ -41,6 +42,9 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashSet;
// TODO: check shape in 2D mode (and handling in renderFill), and group shape mixing 2D and 2D child shapes.
/**
* This class holds a 3D model composed of vertices, normals, colors (per vertex) and
* texture coordinates (also per vertex). All this data is stored in Vertex Buffer Objects
@@ -156,6 +160,22 @@ public class PShape3D extends PShape {
protected float curveVertices[][];
protected int curveVertexCount;
// ........................................................
// Fill color
/** true if fill() is enabled, (read-only) */
public boolean fill;
/** fill that was last set (read-only) */
public int fillColor = 0xffFFFFFF;
protected boolean fillAlpha;
protected float fillR, fillG, fillB, fillA;
protected int fillRi, fillGi, fillBi, fillAi;
public PShape3D(PApplet parent, int family) {
ogl = (PGraphicsOpenGL)parent.g;
@@ -305,28 +325,9 @@ public class PShape3D extends PShape {
currentNormal[2] = nz;
}
public void noFill() {
fill(0, 0, 0, 0);
}
public void fill(float r, float g, float b, float a) {
currentColor[0] = r;
currentColor[1] = g;
currentColor[2] = b;
currentColor[3] = a;
}
public void stroke(float r, float g, float b, float a) {
currentStroke[0] = r;
currentStroke[1] = g;
currentStroke[2] = b;
currentStroke[3] = a;
}
public void noStroke() {
stroke(0, 0, 0, 0);
}
public void strokeWeight(float w) {
currentStroke[4] = w;
@@ -344,6 +345,316 @@ public class PShape3D extends PShape {
// ?
}
//////////////////////////////////////////////////////////////
// FILL COLOR
/*
public void noFill() {
fill = false;
}
public void fill(int rgb) {
colorCalc(rgb);
fillFromCalc();
}
public void fill(int rgb, float alpha) {
colorCalc(rgb, alpha);
fillFromCalc();
}
public void fill(float gray) {
colorCalc(gray);
fillFromCalc();
}
public void fill(float gray, float alpha) {
colorCalc(gray, alpha);
fillFromCalc();
}
public void fill(float x, float y, float z) {
colorCalc(x, y, z);
fillFromCalc();
}
public void fill(float x, float y, float z, float a) {
colorCalc(x, y, z, a);
fillFromCalc();
}
protected void fillFromCalc() {
fill = true;
fillR = calcR;
fillG = calcG;
fillB = calcB;
fillA = calcA;
fillRi = calcRi;
fillGi = calcGi;
fillBi = calcBi;
fillAi = calcAi;
fillColor = calcColor;
fillAlpha = calcAlpha;
}
*/
public void noFill() {
fill(0, 0, 0, 0);
}
public void fill(float r, float g, float b, float a) {
currentColor[0] = r;
currentColor[1] = g;
currentColor[2] = b;
currentColor[3] = a;
}
//////////////////////////////////////////////////////////////
// STROKE CAP/JOIN/WEIGHT
/*
public void strokeWeight(float weight) {
strokeWeight = weight;
}
public void strokeJoin(int join) {
strokeJoin = join;
}
public void strokeCap(int cap) {
strokeCap = cap;
}
*/
//////////////////////////////////////////////////////////////
// STROKE COLOR
public void stroke(float r, float g, float b, float a) {
currentStroke[0] = r;
currentStroke[1] = g;
currentStroke[2] = b;
currentStroke[3] = a;
}
public void noStroke() {
stroke(0, 0, 0, 0);
}
/*
public void noStroke() {
stroke = false;
}
public void stroke(int rgb) {
colorCalc(rgb);
strokeFromCalc();
}
public void stroke(int rgb, float alpha) {
colorCalc(rgb, alpha);
strokeFromCalc();
}
public void stroke(float gray) {
colorCalc(gray);
strokeFromCalc();
}
public void stroke(float gray, float alpha) {
colorCalc(gray, alpha);
strokeFromCalc();
}
public void stroke(float x, float y, float z) {
colorCalc(x, y, z);
strokeFromCalc();
}
public void stroke(float x, float y, float z, float alpha) {
colorCalc(x, y, z, alpha);
strokeFromCalc();
}
protected void strokeFromCalc() {
stroke = true;
strokeR = calcR;
strokeG = calcG;
strokeB = calcB;
strokeA = calcA;
strokeRi = calcRi;
strokeGi = calcGi;
strokeBi = calcBi;
strokeAi = calcAi;
strokeColor = calcColor;
strokeAlpha = calcAlpha;
}
*/
/*
public void imageMode(int mode) {
if ((mode == CORNER) || (mode == CORNERS) || (mode == CENTER)) {
imageMode = mode;
} else {
String msg =
"imageMode() only works with CORNER, CORNERS, or CENTER";
throw new RuntimeException(msg);
}
}
*/
//////////////////////////////////////////////////////////////
// STYLE
/*
public void pushStyle() {
if (styleStackDepth == styleStack.length) {
styleStack = (PStyle[]) PApplet.expand(styleStack);
}
if (styleStack[styleStackDepth] == null) {
styleStack[styleStackDepth] = new PStyle();
}
PStyle s = styleStack[styleStackDepth++];
getStyle(s);
}
public void popStyle() {
if (styleStackDepth == 0) {
throw new RuntimeException("Too many popStyle() without enough pushStyle()");
}
styleStackDepth--;
style(styleStack[styleStackDepth]);
}
public void style(PStyle s) {
// if (s.smooth) {
// smooth();
// } else {
// noSmooth();
// }
imageMode(s.imageMode);
rectMode(s.rectMode);
ellipseMode(s.ellipseMode);
shapeMode(s.shapeMode);
if (s.tint) {
tint(s.tintColor);
} else {
noTint();
}
if (s.fill) {
fill(s.fillColor);
} else {
noFill();
}
if (s.stroke) {
stroke(s.strokeColor);
} else {
noStroke();
}
strokeWeight(s.strokeWeight);
strokeCap(s.strokeCap);
strokeJoin(s.strokeJoin);
// Set the colorMode() for the material properties.
// TODO this is really inefficient, need to just have a material() method,
// but this has the least impact to the API.
colorMode(RGB, 1);
ambient(s.ambientR, s.ambientG, s.ambientB);
emissive(s.emissiveR, s.emissiveG, s.emissiveB);
specular(s.specularR, s.specularG, s.specularB);
shininess(s.shininess);
// material(s.ambientR, s.ambientG, s.ambientB,
// s.emissiveR, s.emissiveG, s.emissiveB,
// s.specularR, s.specularG, s.specularB,
// s.shininess);
// Set this after the material properties.
colorMode(s.colorMode,
s.colorModeX, s.colorModeY, s.colorModeZ, s.colorModeA);
// This is a bit asymmetric, since there's no way to do "noFont()",
// and a null textFont will produce an error (since usually that means that
// the font couldn't load properly). So in some cases, the font won't be
// 'cleared' to null, even though that's technically correct.
if (s.textFont != null) {
textFont(s.textFont, s.textSize);
textLeading(s.textLeading);
}
// These don't require a font to be set.
textAlign(s.textAlign, s.textAlignY);
textMode(s.textMode);
}
public PStyle getStyle() { // ignore
return getStyle(null);
}
public PStyle getStyle(PStyle s) { // ignore
if (s == null) {
s = new PStyle();
}
s.imageMode = imageMode;
s.rectMode = rectMode;
s.ellipseMode = ellipseMode;
s.shapeMode = shapeMode;
s.colorMode = colorMode;
s.colorModeX = colorModeX;
s.colorModeY = colorModeY;
s.colorModeZ = colorModeZ;
s.colorModeA = colorModeA;
s.tint = tint;
s.tintColor = tintColor;
s.fill = fill;
s.fillColor = fillColor;
s.stroke = stroke;
s.strokeColor = strokeColor;
s.strokeWeight = strokeWeight;
s.strokeCap = strokeCap;
s.strokeJoin = strokeJoin;
s.ambientR = ambientR;
s.ambientG = ambientG;
s.ambientB = ambientB;
s.specularR = specularR;
s.specularG = specularG;
s.specularB = specularB;
s.emissiveR = emissiveR;
s.emissiveG = emissiveG;
s.emissiveB = emissiveB;
s.shininess = shininess;
s.textFont = textFont;
s.textAlign = textAlign;
s.textAlignY = textAlignY;
s.textMode = textMode;
s.textSize = textSize;
s.textLeading = textLeading;
return s;
}
*/
///////////////////////////////////////////////////////////
//