/*
* 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.render;
import java.net.URI;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import com.jaamsim.math.AABB;
import com.jaamsim.math.Mat4d;
import com.jaamsim.math.Ray;
import com.jaamsim.math.Transform;
import com.jaamsim.math.Vec3d;
import com.jaamsim.math.Vec4d;
import com.jogamp.opengl.GL2GL3;
/**
* TextureView is a simple renderable that allows a rectangular image to be easily displayed in 3-space
* @author Matt.Chudleigh
*
*/
public class TextureView implements Renderable {
private final URI _imageURI;
private final Transform _trans;
private final Vec3d _scale;
private final long _pickingID;
private final AABB _bounds;
private final boolean _isTransparent;
private final boolean _isCompressed;
private final VisibilityInfo _visInfo;
// Initialize the very simple buffers needed to render this image
static private boolean staticInit = false;
static private int vertBuff;
static private int texCoordBuff;
static private int normalBuff;
static private HashMap<Integer, Integer> VAOMap = new HashMap<>();
static private int progHandle;
static private int projMatVar;
static private int modelViewMatVar;
static private int normalMatVar;
static private int bindSpaceMatVar;
static private int bindSpaceNorMatVar;
static private int texVar;
static private int lightDirVar;
static private int lightIntVar;
static private int numLightsVar;
static private int specColorVar;
static private int ambientColorVar;
static private int shininessVar;
static private int cVar;
static private int fcVar;
static private float[] lightDir = new float[3];
static private float[] lightInt = new float[1];
// a column-major identity matrix
static private float[] identMat = new float[16];
static {
identMat[ 0] = 1.0f; identMat[ 4] = 0.0f; identMat[ 8] = 0.0f; identMat[12] = 0.0f;
identMat[ 1] = 0.0f; identMat[ 5] = 1.0f; identMat[ 9] = 0.0f; identMat[13] = 0.0f;
identMat[ 2] = 0.0f; identMat[ 6] = 0.0f; identMat[10] = 1.0f; identMat[14] = 0.0f;
identMat[ 3] = 0.0f; identMat[ 7] = 0.0f; identMat[11] = 0.0f; identMat[15] = 1.0f;
}
public TextureView(URI imageURI, Transform trans, Vec3d scale, boolean isTransparent, boolean isCompressed,
VisibilityInfo visInfo, long pickingID) {
_imageURI = imageURI;
_trans = trans;
_scale = scale;
_scale.z = 1; // This object can only be scaled in X, Y
_pickingID = pickingID;
_isTransparent = isTransparent;
_isCompressed = isCompressed;
_visInfo = visInfo;
Mat4d modelMat = RenderUtils.mergeTransAndScale(_trans, _scale);
ArrayList<Vec4d> vs = new ArrayList<>(4);
vs.add(new Vec4d( 0.5, 0.5, 0, 1.0d));
vs.add(new Vec4d(-0.5, 0.5, 0, 1.0d));
vs.add(new Vec4d(-0.5, -0.5, 0, 1.0d));
vs.add(new Vec4d( 0.5, -0.5, 0, 1.0d));
_bounds = new AABB(vs, modelMat);
}
private static void initStaticBuffers(Renderer r) {
GL2GL3 gl = r.getGL();
int[] buffs = new int[3];
gl.glGenBuffers(3, buffs, 0);
vertBuff = buffs[0];
texCoordBuff = buffs[1];
normalBuff = buffs[2];
FloatBuffer verts = FloatBuffer.allocate(6*3); // 2 triangles * 3 coordinates
verts.put(-0.5f); verts.put(-0.5f); verts.put(0.0f);
verts.put( 0.5f); verts.put(-0.5f); verts.put(0.0f);
verts.put( 0.5f); verts.put( 0.5f); verts.put(0.0f);
verts.put(-0.5f); verts.put(-0.5f); verts.put(0.0f);
verts.put( 0.5f); verts.put( 0.5f); verts.put(0.0f);
verts.put(-0.5f); verts.put( 0.5f); verts.put(0.0f);
verts.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, vertBuff);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, 6*3*4, verts, GL2GL3.GL_STATIC_DRAW);
FloatBuffer texCoords = FloatBuffer.allocate(6*2); // 2 triangles * 2 coordinates
texCoords.put(0.0f); texCoords.put(0.0f);
texCoords.put(1.0f); texCoords.put(0.0f);
texCoords.put(1.0f); texCoords.put(1.0f);
texCoords.put(0.0f); texCoords.put(0.0f);
texCoords.put(1.0f); texCoords.put(1.0f);
texCoords.put(0.0f); texCoords.put(1.0f);
texCoords.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, texCoordBuff);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, 6*2*4, texCoords, GL2GL3.GL_STATIC_DRAW);
FloatBuffer normals = FloatBuffer.allocate(6*3); // 2 triangles * 3 coordinates
for (int i = 0; i < 6; ++i) {
normals.put(0.0f); normals.put(0.0f); normals.put(1.0f);
}
normals.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, normalBuff);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, 6*3*4, normals, GL2GL3.GL_STATIC_DRAW);
// Initialize the shader variables
progHandle = r.getMeshShader(Renderer.DIFF_TEX_FLAG).getProgramHandle();
modelViewMatVar = gl.glGetUniformLocation(progHandle, "modelViewMat");
projMatVar = gl.glGetUniformLocation(progHandle, "projMat");
normalMatVar = gl.glGetUniformLocation(progHandle, "normalMat");
bindSpaceMatVar = gl.glGetUniformLocation(progHandle, "bindSpaceMat");
bindSpaceNorMatVar = gl.glGetUniformLocation(progHandle, "bindSpaceNorMat");
texVar = gl.glGetUniformLocation(progHandle, "diffuseTex");
lightDirVar = gl.glGetUniformLocation(progHandle, "lightDir");
lightIntVar = gl.glGetUniformLocation(progHandle, "lightIntensity");
numLightsVar = gl.glGetUniformLocation(progHandle, "numLights");
specColorVar = gl.glGetUniformLocation(progHandle, "specColor");
ambientColorVar = gl.glGetUniformLocation(progHandle, "ambientColor");
shininessVar = gl.glGetUniformLocation(progHandle, "shininess");
cVar = gl.glGetUniformLocation(progHandle, "C");
fcVar = gl.glGetUniformLocation(progHandle, "FC");
lightDir[0] = 0;
lightDir[1] = 0;
lightDir[2] = -1;
lightInt[0] = 1;
staticInit = true;
}
private void setupVAO(int contextID, Renderer renderer) {
GL2GL3 gl = renderer.getGL();
int vao = renderer.generateVAO(contextID, gl);
VAOMap.put(contextID, vao);
gl.glBindVertexArray(vao);
// Position
int posVar = gl.glGetAttribLocation(progHandle, "position");
gl.glEnableVertexAttribArray(posVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, vertBuff);
gl.glVertexAttribPointer(posVar, 3, GL2GL3.GL_FLOAT, false, 0, 0);
// Normals
int normalVar = gl.glGetAttribLocation(progHandle, "normal");
gl.glEnableVertexAttribArray(normalVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, normalBuff);
gl.glVertexAttribPointer(normalVar, 3, GL2GL3.GL_FLOAT, false, 0, 0);
// TexCoords
int texCoordVar = gl.glGetAttribLocation(progHandle, "texCoord");
gl.glEnableVertexAttribArray(texCoordVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, texCoordBuff);
gl.glVertexAttribPointer(texCoordVar, 2, GL2GL3.GL_FLOAT, false, 0, 0);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, 0);
gl.glBindVertexArray(0);
}
@Override
public void render(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
if (_isTransparent) {
// Return, this will be handled in the transparent render phase
return;
}
renderImp(contextID, renderer, cam, pickRay);
}
private void renderImp(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
if (!staticInit) {
initStaticBuffers(renderer);
}
GL2GL3 gl = renderer.getGL();
int textureID = renderer.getTexCache().getTexID(gl, _imageURI, _isTransparent, _isCompressed, false);
if (textureID == TexCache.LOADING_TEX_ID) {
return; // This texture is not ready yet
}
if (!VAOMap.containsKey(contextID)) {
setupVAO(contextID, renderer);
}
int vao = VAOMap.get(contextID);
gl.glBindVertexArray(vao);
Mat4d modelViewMat = new Mat4d();
cam.getViewMat4d(modelViewMat);
modelViewMat.mult4(_trans.getMat4dRef());
modelViewMat.scaleCols3(_scale);
Mat4d normalMat = RenderUtils.getInverseWithScale(_trans, _scale);
normalMat.transpose4();
gl.glUseProgram(progHandle);
gl.glUniformMatrix4fv(modelViewMatVar, 1, false, RenderUtils.MarshalMat4d(modelViewMat), 0);
gl.glUniformMatrix4fv(projMatVar, 1, false, RenderUtils.MarshalMat4d(cam.getProjMat4d()), 0);
gl.glUniformMatrix4fv(normalMatVar, 1, false, RenderUtils.MarshalMat4d(normalMat), 0);
gl.glUniformMatrix4fv(bindSpaceMatVar, 1, false, identMat, 0);
gl.glUniformMatrix4fv(bindSpaceNorMatVar, 1, false, identMat, 0);
gl.glUniform1f(cVar, Camera.C);
gl.glUniform1f(fcVar, Camera.FC);
gl.glUniform1i(numLightsVar, 1);
gl.glUniform3fv(lightDirVar, 1, lightDir, 0);
gl.glUniform1fv(lightIntVar, 1, lightInt, 0);
gl.glUniform3f(ambientColorVar, 0.0f, 0.0f, 0.0f);
gl.glUniform3f(specColorVar, 0.0f, 0.0f, 0.0f);
gl.glUniform1f(shininessVar, 1.0f);
gl.glActiveTexture(GL2GL3.GL_TEXTURE0);
gl.glBindTexture(GL2GL3.GL_TEXTURE_2D, textureID);
gl.glUniform1i(texVar, 0);
gl.glTexParameteri(GL2GL3.GL_TEXTURE_2D, GL2GL3.GL_TEXTURE_WRAP_S, GL2GL3.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL2GL3.GL_TEXTURE_2D, GL2GL3.GL_TEXTURE_WRAP_T, GL2GL3.GL_CLAMP_TO_EDGE);
// Draw
gl.glDisable(GL2GL3.GL_CULL_FACE);
gl.glDrawArrays(GL2GL3.GL_TRIANGLES, 0, 6);
gl.glEnable(GL2GL3.GL_CULL_FACE);
gl.glBindVertexArray(0);
}
@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
}
@Override
public boolean hasTransparent() {
return _isTransparent;
}
@Override
public void renderTransparent(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
renderImp(contextID, renderer, cam, pickRay);
}
@Override
public boolean renderForView(int viewID, Camera cam) {
double dist = cam.distToBounds(getBoundsRef());
return _visInfo.isVisible(viewID, dist);
}
}