/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.font;
import java.util.ArrayList;
import java.util.HashMap;
import com.jogamp.opengl.GL2GL3;
import com.jaamsim.math.AABB;
import com.jaamsim.math.Color4d;
import com.jaamsim.math.Mat4d;
import com.jaamsim.math.Ray;
import com.jaamsim.math.Transform;
import com.jaamsim.math.Vec4d;
import com.jaamsim.render.Camera;
import com.jaamsim.render.RenderUtils;
import com.jaamsim.render.Renderable;
import com.jaamsim.render.Renderer;
import com.jaamsim.render.Shader;
import com.jaamsim.render.VisibilityInfo;
public class TessString implements Renderable {
private TessFont _font;
private int[] _contents;
private final float[] _color;
private long _pickingID;
/**
* The transform to global space (will be gone when the scene graph is finalized)
*/
private Mat4d _trans;
private AABB _bounds;
private VisibilityInfo _visInfo;
// Cached data needed to draw this string, prevents hitting the font character map
private int[] starts;
private int[] numVerts;
private double[] advances;
private static HashMap<Integer, Integer> VAOMap = new HashMap<>();
public TessString(TessFont font, String contents, Color4d color,
Transform trans, double textHeight, VisibilityInfo visInfo, long pickingID) {
this(font, contents, color, trans.getMat4dRef(), textHeight, visInfo, pickingID);
}
public TessString(TessFont font, String contents, Color4d color,
Mat4d trans, double textHeight, VisibilityInfo visInfo, long pickingID) {
_font = font;
_color = color.toFloats();
_trans = new Mat4d(trans);
_visInfo = visInfo;
// Adjust the scale factor of the transform to account for the requested text height
double heightScale = textHeight / _font.getNominalHeight();
_trans.scale3(heightScale);
//_trans.setScale( trans.getScale() * heightScale);
_pickingID = pickingID;
starts = new int[contents.length()];
numVerts = new int[contents.length()];
advances = new double[contents.length()];
double width = 0;
double height = _font.getNominalHeight();
_contents = RenderUtils.stringToCodePoints(contents);
for (int i = 0; i < _contents.length; ++i) {
TessChar tc = _font.getTessChar(_contents[i]);
assert(tc != null);
width += tc.getAdvance();
starts[i] = tc.getStartIndex();
numVerts[i] = tc.getNumVerts();
advances[i] = tc.getAdvance();
}
// As the renderer draws characters from the bottom left, but the model specifies text labels in the center,
// we need to offset the transform
Mat4d align = new Mat4d();
align.setTranslate3(new Vec4d(-width/2, -height/2, 0, 1.0d));
_trans.mult4(align);
ArrayList<Vec4d> vs = new ArrayList<>(4);
vs.add(new Vec4d( width, height, 0, 1.0d));
vs.add(new Vec4d( 0, height, 0, 1.0d));
vs.add(new Vec4d( 0, 0, 0, 1.0d));
vs.add(new Vec4d( width, 0, 0, 1.0d));
_bounds = new AABB(vs, _trans);
}
@Override
public void render(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
GL2GL3 gl = renderer.getGL();
if (!VAOMap.containsKey(contextID)) {
setupVAO(contextID, renderer);
}
int vao = VAOMap.get(contextID);
gl.glBindVertexArray(vao);
// Render the string
Shader s = renderer.getShader(Renderer.ShaderHandle.FONT);
s.useShader(gl);
int prog = s.getProgramHandle();
// Setup uniforms for this object
Mat4d modelViewProjMat = new Mat4d();
cam.getViewMat4d(modelViewProjMat);
modelViewProjMat.mult4(_trans);
Mat4d projMat = cam.getProjMat4d();
modelViewProjMat.mult4(projMat, modelViewProjMat);
int modelViewProjMatVar = gl.glGetUniformLocation(prog, "modelViewProjMat");
gl.glUniformMatrix4fv(modelViewProjMatVar, 1, false, RenderUtils.MarshalMat4d(modelViewProjMat), 0);
int colorVar = gl.glGetUniformLocation(prog, "color");
gl.glUniform4fv(colorVar, 1, _color, 0);
int cVar = gl.glGetUniformLocation(prog, "C");
gl.glUniform1f(cVar, Camera.C);
int fcVar = gl.glGetUniformLocation(prog, "FC");
gl.glUniform1f(fcVar, Camera.FC);
int advanceVar = gl.glGetUniformLocation(prog, "advance");
int posVar = gl.glGetAttribLocation(prog, "position");
gl.glEnableVertexAttribArray(posVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, _font.getGLBuffer(gl));
gl.glVertexAttribPointer(posVar, 2, GL2GL3.GL_FLOAT, false, 0, 0);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, 0);
// Send out one draw call per character
float advance = 0;
gl.glDisable(GL2GL3.GL_CULL_FACE);
for (int i = 0; i < _contents.length; ++i) {
gl.glUniform1f(advanceVar, advance);
gl.glDrawArrays(GL2GL3.GL_TRIANGLES, starts[i], numVerts[i]);
advance += advances[i];
}
gl.glEnable(GL2GL3.GL_CULL_FACE);
// Cleanup
gl.glDisableVertexAttribArray(posVar);
}
private void setupVAO(int contextID, Renderer renderer) {
GL2GL3 gl = renderer.getGL();
int vao = renderer.generateVAO(contextID, gl);
VAOMap.put(contextID, vao);
}
@Override
public long getPickingID() {
return _pickingID;
}
@Override
public AABB getBoundsRef() {
return _bounds;
}
@Override
public double getCollisionDist(Ray r, boolean precise)
{
return _bounds.collisionDist(r);
// TODO precise collision
}
@Override
public boolean hasTransparent() {
return false;
}
@Override
public void renderTransparent(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
}
@Override
public boolean renderForView(int viewID, Camera cam) {
double dist = cam.distToBounds(getBoundsRef());
return _visInfo.isVisible(viewID, dist);
}
} // class