/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
* Copyright (C) 2015 JaamSim Software 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.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import com.jaamsim.MeshFiles.MeshData;
import com.jaamsim.math.AABB;
import com.jaamsim.math.Color4d;
import com.jaamsim.math.ConvexHull;
import com.jaamsim.math.Mat4d;
import com.jaamsim.math.Vec2d;
import com.jaamsim.math.Vec3d;
import com.jaamsim.math.Vec4d;
import com.jaamsim.ui.LogBox;
import com.jogamp.opengl.GL2GL3;
import com.jogamp.opengl.GLException;
/**
* A basic wrapper around mesh data and the OpenGL calls that go with it
* Will maintain a vertex buffer for vertex data, texture coordinates and normal data
* The first pass is non-indexed but will be updated in the future
* @author Matt Chudleigh
*
*/
public class MeshProto {
/** Sortable entry for a single transparent sub mesh before rendering
*
* @author matt.chudleigh
*
*/
private static class TransSortable implements Comparable<TransSortable> {
public SubMesh subMesh;
public double dist;
public int matIndex;
public Mat4d transform;
public Mat4d invTrans;
@Override
public int compareTo(TransSortable o) {
// Sort such that largest distance sorts to front of list
// by reversing argument order in compare.
return Double.compare(o.dist, this.dist);
}
}
private static class ShaderInfo {
private int meshProgHandle;
private int modelViewMatVar;
private int projMatVar;
private int bindSpaceMatVar;
private int bindSpaceNorMatVar;
private int normalMatVar;
private int texVar;
private int diffuseColorVar;
private int specColorVar;
private int ambientColorVar;
private int shininessVar;
private int lightDirVar;
private int lightIntVar;
private int numLightsVar;
private int cVar;
private int fcVar;
}
private static ShaderInfo[] sInfos = new ShaderInfo[Renderer.NUM_MESH_SHADERS];
private static Vec4d[] lightsDir = new Vec4d[2];
private static Vec4d[] lightsDirScratch = new Vec4d[2];
private static float[] lightsDirFloats = new float[6];
private static float[] lightsInt = new float[2];
private static int numLights;
private static class SubMesh {
public int _vertexBuffer;
public int _texCoordBuffer;
public int _normalBuffer;
public int _indexBuffer;
public Vec3d _center;
public int _numVerts;
public final HashMap<Integer, Integer>[] vaoMaps;
@SuppressWarnings("unchecked") // initializing the vaoMaps array
SubMesh(int[] usedShaders) {
vaoMaps = new HashMap[Renderer.NUM_MESH_SHADERS];
for (int i = 0; i < usedShaders.length; ++i) {
int shaderID = usedShaders[i];
vaoMaps[shaderID] = new HashMap<>();
}
}
}
private static class Material {
public int _texHandle;
public Color4d _diffuseColor;
public Color4d _specColor = new Color4d();
public Color4d _ambientColor = new Color4d();
public double _shininess;
public int _transType;
public Color4d _transColour;
int shaderID;
}
private static class SubLine {
public int _vertexBuffer;
public Color4d _diffuseColor;
public int _progHandle;
public int _modelViewMatVar;
public int _projMatVar;
public int _colorVar;
public int _cVar;
public int _fcVar;
public ConvexHull _hull;
public int _numVerts;
public HashMap<Integer, Integer> vaoMap = new HashMap<>();
}
private final MeshData data;
private final ArrayList<SubMesh> _subMeshes;
private final ArrayList<SubLine> _subLines;
private final ArrayList<Material> _materials;
private final int[] usedShaders;
/**
* The maximum distance a vertex is from the origin
*/
private boolean _isLoadedGPU = false;
private final boolean flattenBuffers;
public MeshProto(MeshData data, boolean flattenBuffers) {
this.flattenBuffers = flattenBuffers;
this.data = data;
_subMeshes = new ArrayList<>();
_subLines = new ArrayList<>();
_materials = new ArrayList<>();
usedShaders = data.getUsedMeshShaders();
}
public void render(int contextID, Renderer renderer,
Mat4d modelMat,
Mat4d invModelMat,
Camera cam,
MeshData.Pose pose) {
assert(_isLoadedGPU);
Mat4d viewMat = new Mat4d();
cam.getViewMat4d(viewMat);
Mat4d normalMat = new Mat4d(invModelMat);
normalMat.transpose4();
Mat4d finalNorMat = new Mat4d(); // The normal matrix in eye space
cam.getRotMat4d(finalNorMat);
finalNorMat.mult4(finalNorMat, normalMat);
Mat4d modelViewMat = new Mat4d();
modelViewMat.mult4(viewMat, modelMat);
initUniforms(renderer, modelViewMat, cam.getProjMat4d(), viewMat, finalNorMat);
for (MeshData.StaticMeshInstance subInst : data.getStaticMeshInstances()) {
renderOpaqueSubMesh(contextID, renderer, subInst.subMeshIndex, subInst.materialIndex, cam,
modelMat, invModelMat, modelViewMat,
subInst.transform, subInst.invTrans);
}
for (MeshData.AnimMeshInstance subInst : data.getAnimMeshInstances()) {
renderOpaqueSubMesh(contextID, renderer, subInst.meshIndex, subInst.materialIndex, cam,
modelMat, invModelMat, modelViewMat,
pose.transforms[subInst.nodeIndex], pose.invTransforms[subInst.nodeIndex]);
}
for (MeshData.StaticLineInstance subInst : data.getStaticLineInstances()) {
SubLine subLine = _subLines.get(subInst.lineIndex);
renderSubLine(subLine, contextID, renderer, modelMat, modelViewMat, subInst.transform, cam);
}
for (MeshData.AnimLineInstance subInst : data.getAnimLineInstances()) {
SubLine subLine = _subLines.get(subInst.lineIndex);
renderSubLine(subLine, contextID, renderer, modelMat, modelViewMat, pose.transforms[subInst.nodeIndex], cam);
}
}
private void renderOpaqueSubMesh(int contextID, Renderer renderer,
int meshIndex, int matIndex,
Camera cam,
Mat4d modelMat, Mat4d invModelMat, Mat4d modelViewMat,
Mat4d subTrans, Mat4d invSubTrans) {
Mat4d fullInvMat = new Mat4d();
fullInvMat.mult4(invSubTrans, invModelMat);
SubMesh subMesh = _subMeshes.get(meshIndex);
Material mat = _materials.get(matIndex);
if (mat._transType != MeshData.NO_TRANS) {
return; // Render transparent submeshes after
}
Mat4d camNormal = new Mat4d();
camNormal.mult4(modelMat, subTrans);
camNormal.transpose4();
MeshData.SubMeshData subData = data.getSubMeshData().get(meshIndex);
if (!cam.collides(subData.localBounds, fullInvMat, camNormal)) {
// Not in the view frustum
return;
}
Mat4d fullModelViewMat = new Mat4d();
fullModelViewMat.mult4(modelViewMat, subTrans);
Vec3d boundsMax = new Vec3d();
Vec3d boundsMin = new Vec3d();
Vec3d diff = new Vec3d();
boundsMax.multAndTrans3(fullModelViewMat, subData.localBounds.maxPt);
boundsMin.multAndTrans3(fullModelViewMat, subData.localBounds.minPt);
diff.sub3(boundsMax, boundsMin);
double radius = diff.mag3();
double dist = Math.abs(boundsMax.z + boundsMin.z)/2.0;
double apparentSize = radius / dist;
if (apparentSize < 0.001) {
// This object is too small to draw
return;
}
renderSubMesh(subMesh, matIndex, subTrans, invSubTrans, contextID, renderer);
}
public void renderTransparent(int contextID, Renderer renderer,
Mat4d modelMat,
Mat4d invModelMat,
Camera cam,
MeshData.Pose pose) {
Mat4d viewMat = new Mat4d();
cam.getViewMat4d(viewMat);
Mat4d normalMat = new Mat4d(invModelMat);
normalMat.transpose4();
Mat4d finalNorMat = new Mat4d(); // The normal matrix in eye space
cam.getRotMat4d(finalNorMat);
finalNorMat.mult4(finalNorMat, normalMat);
Mat4d modelViewMat = new Mat4d();
modelViewMat.mult4(viewMat, modelMat);
Mat4d subModelView = new Mat4d();
ArrayList<TransSortable> transparents = new ArrayList<>();
for (MeshData.StaticMeshInstance subInst : data.getStaticMeshInstances()) {
SubMesh subMesh = _subMeshes.get(subInst.subMeshIndex);
Material mat = _materials.get(subInst.materialIndex);
if (mat._transType == MeshData.NO_TRANS) {
continue; // Opaque sub meshes have been rendered
}
Mat4d fullInvMat = new Mat4d();
fullInvMat.mult4(subInst.invTrans, invModelMat);
Mat4d camNormal = new Mat4d();
camNormal.mult4(modelMat, subInst.transform);
camNormal.transpose4();
MeshData.SubMeshData subData = data.getSubMeshData().get(subInst.subMeshIndex);
if (!cam.collides(subData.localBounds, fullInvMat, camNormal)) {
continue;
}
subModelView.mult4(modelViewMat, subInst.transform);
Vec3d eyeCenter = new Vec3d();
eyeCenter.multAndTrans3(subModelView, subMesh._center);
TransSortable ts = new TransSortable();
ts.subMesh = subMesh;
ts.dist = eyeCenter.z;
ts.matIndex = subInst.materialIndex;
ts.transform = subInst.transform;
ts.invTrans = subInst.invTrans;
transparents.add(ts);
}
for (MeshData.AnimMeshInstance subInst : data.getAnimMeshInstances()) {
SubMesh subMesh = _subMeshes.get(subInst.meshIndex);
Material mat = _materials.get(subInst.materialIndex);
if (mat._transType == MeshData.NO_TRANS) {
continue; // Opaque sub meshes have been rendered
}
Mat4d subTrans = pose.transforms[subInst.nodeIndex];
Mat4d invSubTrans = pose.invTransforms[subInst.nodeIndex];
Mat4d fullInvMat = new Mat4d();
fullInvMat.mult4(invSubTrans, invModelMat);
Mat4d camNormal = new Mat4d();
camNormal.mult4(modelMat, subTrans);
camNormal.transpose4();
MeshData.SubMeshData subData = data.getSubMeshData().get(subInst.meshIndex);
if (!cam.collides(subData.localBounds, fullInvMat, camNormal)) {
continue;
}
subModelView.mult4(modelViewMat, subTrans);
Vec3d eyeCenter = new Vec3d();
eyeCenter.multAndTrans3(subModelView, subMesh._center);
TransSortable ts = new TransSortable();
ts.subMesh = subMesh;
ts.dist = eyeCenter.z;
ts.matIndex = subInst.materialIndex;
ts.transform = subTrans;
ts.invTrans = invSubTrans;
transparents.add(ts);
}
initUniforms(renderer, modelViewMat, cam.getProjMat4d(), viewMat, finalNorMat);
Collections.sort(transparents);
for (TransSortable ts : transparents) {
Mat4d subInstNorm = new Mat4d(ts.invTrans);
subInstNorm.transpose4();
renderSubMesh(ts.subMesh, ts.matIndex, ts.transform,
subInstNorm, contextID, renderer);
}
}
private void initUniforms(Renderer renderer, Mat4d modelViewMat, Mat4d projMat, Mat4d viewMat, Mat4d normalMat) {
GL2GL3 gl = renderer.getGL();
lightsDirScratch[0].mult4(viewMat, lightsDir[0]);
lightsDirScratch[1].mult4(viewMat, lightsDir[1]);
lightsDirFloats[0] = (float)lightsDirScratch[0].x;
lightsDirFloats[1] = (float)lightsDirScratch[0].y;
lightsDirFloats[2] = (float)lightsDirScratch[0].z;
lightsDirFloats[3] = (float)lightsDirScratch[1].x;
lightsDirFloats[4] = (float)lightsDirScratch[1].y;
lightsDirFloats[5] = (float)lightsDirScratch[1].z;
for (int i = 0; i < usedShaders.length; ++i) {
int shaderID = usedShaders[i];
ShaderInfo si = sInfos[shaderID];
gl.glUseProgram(si.meshProgHandle);
gl.glUniformMatrix4fv(si.modelViewMatVar, 1, false, RenderUtils.MarshalMat4d(modelViewMat), 0);
gl.glUniformMatrix4fv(si.projMatVar, 1, false, RenderUtils.MarshalMat4d(projMat), 0);
gl.glUniformMatrix4fv(si.normalMatVar, 1, false, RenderUtils.MarshalMat4d(normalMat), 0);
gl.glUniform3fv(si.lightDirVar, 2, lightsDirFloats, 0);
gl.glUniform1fv(si.lightIntVar, 2, lightsInt, 0);
gl.glUniform1i(si.numLightsVar, numLights);
gl.glUniform1f(si.cVar, Camera.C);
gl.glUniform1f(si.fcVar, Camera.FC);
}
}
private void setupVAOForSubMesh(int contextID, SubMesh sub, Renderer renderer) {
// Setup a VAO for each used shader for this mesh (this could be optimized for only the sub mesh...
for (int shaderID : usedShaders) {
setupVAOForSubMeshImp(contextID, shaderID, sub, renderer);
}
}
private void setupVAOForSubMeshImp(int contextID, int shaderID, SubMesh sub, Renderer renderer) {
GL2GL3 gl = renderer.getGL();
int vao = renderer.generateVAO(contextID, gl);
sub.vaoMaps[shaderID].put(contextID, vao);
gl.glBindVertexArray(vao);
int progHandle = sInfos[shaderID].meshProgHandle;
gl.glUseProgram(progHandle);
int texCoordVar = gl.glGetAttribLocation(progHandle, "texCoord");
// For some shaders the texCoordVar may be optimized away
if (texCoordVar != -1) {
if (sub._texCoordBuffer != 0) {
// Texture coordinates
gl.glEnableVertexAttribArray(texCoordVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._texCoordBuffer);
gl.glVertexAttribPointer(texCoordVar, 2, GL2GL3.GL_FLOAT, false, 0, 0);
} else {
gl.glVertexAttrib2f(texCoordVar, 0, 0);
}
}
int posVar = gl.glGetAttribLocation(progHandle, "position");
gl.glEnableVertexAttribArray(posVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._vertexBuffer);
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, sub._normalBuffer);
gl.glVertexAttribPointer(normalVar, 3, GL2GL3.GL_FLOAT, false, 0, 0);
if (!flattenBuffers) {
gl.glBindBuffer(GL2GL3.GL_ELEMENT_ARRAY_BUFFER, sub._indexBuffer);
}
gl.glBindVertexArray(0);
}
private void renderSubMesh(SubMesh subMesh, int materialIndex,
Mat4d subInstTrans,
Mat4d subInstInvTrans,
int contextID,
Renderer renderer) {
Material mat = _materials.get(materialIndex);
int shaderID = mat.shaderID;
GL2GL3 gl = renderer.getGL();
if (!subMesh.vaoMaps[shaderID].containsKey(contextID)) {
setupVAOForSubMesh(contextID, subMesh, renderer);
}
int vao = subMesh.vaoMaps[shaderID].get(contextID);
gl.glBindVertexArray(vao);
ShaderInfo si = sInfos[shaderID];
gl.glUseProgram(si.meshProgHandle);
// Setup uniforms for this object
Mat4d subInstNorm = new Mat4d(subInstInvTrans);
subInstNorm.transpose4();
gl.glUniformMatrix4fv(si.bindSpaceMatVar, 1, false, RenderUtils.MarshalMat4d(subInstTrans), 0);
gl.glUniformMatrix4fv(si.bindSpaceNorMatVar, 1, false, RenderUtils.MarshalMat4d(subInstNorm), 0);
if (mat._texHandle != 0) {
gl.glActiveTexture(GL2GL3.GL_TEXTURE0);
gl.glBindTexture(GL2GL3.GL_TEXTURE_2D, mat._texHandle);
gl.glTexParameteri(GL2GL3.GL_TEXTURE_2D, GL2GL3.GL_TEXTURE_WRAP_S, GL2GL3.GL_REPEAT);
gl.glTexParameteri(GL2GL3.GL_TEXTURE_2D, GL2GL3.GL_TEXTURE_WRAP_T, GL2GL3.GL_REPEAT);
gl.glUniform1i(si.texVar, 0);
} else {
gl.glUniform4fv(si.diffuseColorVar, 1, mat._diffuseColor.toFloats(), 0);
}
gl.glUniform3fv(si.ambientColorVar, 1, mat._ambientColor.toFloats(), 0);
gl.glUniform3fv(si.specColorVar, 1, mat._specColor.toFloats(), 0);
gl.glUniform1f(si.shininessVar, (float)mat._shininess);
if (mat._transType != MeshData.NO_TRANS) {
gl.glBlendEquationSeparate(GL2GL3.GL_FUNC_ADD, GL2GL3.GL_MAX);
if (mat._transType != MeshData.DIFF_ALPHA_TRANS) {
gl.glBlendColor((float)mat._transColour.r,
(float)mat._transColour.g,
(float)mat._transColour.b,
(float)mat._transColour.a);
}
if (mat._transType == MeshData.A_ONE_TRANS) {
gl.glBlendFuncSeparate(GL2GL3.GL_CONSTANT_ALPHA, GL2GL3.GL_ONE_MINUS_CONSTANT_ALPHA, GL2GL3.GL_ONE, GL2GL3.GL_ZERO);
} else if (mat._transType == MeshData.RGB_ZERO_TRANS) {
gl.glBlendFuncSeparate(GL2GL3.GL_ONE_MINUS_CONSTANT_COLOR, GL2GL3.GL_CONSTANT_COLOR, GL2GL3.GL_ONE, GL2GL3.GL_ZERO);
} else if (mat._transType == MeshData.DIFF_ALPHA_TRANS) {
gl.glBlendFuncSeparate(GL2GL3.GL_SRC_ALPHA, GL2GL3.GL_ONE_MINUS_SRC_ALPHA, GL2GL3.GL_ONE, GL2GL3.GL_ZERO);
} else {
assert(false); // Unknown transparency type
}
}
// Actually draw it
//gl.glPolygonMode(GL2GL3.GL_FRONT_AND_BACK, GL2GL3.GL_LINE);
gl.glDisable(GL2GL3.GL_CULL_FACE);
if (flattenBuffers) {
gl.glDrawArrays(GL2GL3.GL_TRIANGLES, 0, subMesh._numVerts);
} else {
gl.glDrawElements(GL2GL3.GL_TRIANGLES, subMesh._numVerts, GL2GL3.GL_UNSIGNED_INT, 0);
}
gl.glEnable(GL2GL3.GL_CULL_FACE);
// Reset the blend state
if (mat._transType != MeshData.NO_TRANS) {
gl.glBlendEquationSeparate(GL2GL3.GL_FUNC_ADD, GL2GL3.GL_MAX);
gl.glBlendFuncSeparate(GL2GL3.GL_SRC_ALPHA, GL2GL3.GL_ONE_MINUS_SRC_ALPHA, GL2GL3.GL_ONE, GL2GL3.GL_ONE);
}
gl.glBindVertexArray(0);
}
private void setupVAOForSubLine(int contextID, SubLine sub, Renderer renderer) {
GL2GL3 gl = renderer.getGL();
int vao = renderer.generateVAO(contextID, gl);
sub.vaoMap.put(contextID, vao);
gl.glBindVertexArray(vao);
int prog = sub._progHandle;
gl.glUseProgram(prog);
int posVar = gl.glGetAttribLocation(prog, "position");
gl.glEnableVertexAttribArray(posVar);
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._vertexBuffer);
gl.glVertexAttribPointer(posVar, 3, GL2GL3.GL_FLOAT, false, 0, 0);
gl.glBindVertexArray(0);
}
private void renderSubLine(SubLine sub, int contextID, Renderer renderer,
Mat4d modelMat, Mat4d modelViewMat, Mat4d subInstTrans, Camera cam) {
Mat4d subModelViewMat = new Mat4d();
Mat4d subModelMat = new Mat4d();
subModelMat.mult4(modelMat, subInstTrans);
AABB instBounds = sub._hull.getAABB(subModelMat);
if (!cam.collides(instBounds)) {
return;
}
subModelViewMat.mult4(modelViewMat, subInstTrans);
GL2GL3 gl = renderer.getGL();
if (!sub.vaoMap.containsKey(contextID)) {
setupVAOForSubLine(contextID, sub, renderer);
}
int vao = sub.vaoMap.get(contextID);
gl.glBindVertexArray(vao);
int prog = sub._progHandle;
gl.glUseProgram(prog);
Mat4d projMat = cam.getProjMat4d();
gl.glUniformMatrix4fv(sub._modelViewMatVar, 1, false, RenderUtils.MarshalMat4d(subModelViewMat), 0);
gl.glUniformMatrix4fv(sub._projMatVar, 1, false, RenderUtils.MarshalMat4d(projMat), 0);
gl.glUniform4fv(sub._colorVar, 1, sub._diffuseColor.toFloats(), 0);
gl.glUniform1f(sub._cVar, Camera.C);
gl.glUniform1f(sub._fcVar, Camera.FC);
gl.glLineWidth(1);
// Actually draw it
gl.glDrawArrays(GL2GL3.GL_LINES, 0, sub._numVerts);
gl.glBindVertexArray(0);
}
/**
* Push all the data to the GPU and free up CPU side ram
* @param gl
*/
public void loadGPUAssets(GL2GL3 gl, Renderer renderer) {
assert(!_isLoadedGPU);
try {
for (MeshData.SubMeshData subData : data.getSubMeshData()) {
loadGPUSubMesh(gl, renderer, subData);
}
for (MeshData.SubLineData subData : data.getSubLineData()) {
loadGPUSubLine(gl, renderer, subData);
}
for (MeshData.Material mat : data.getMaterials()) {
loadGPUMaterial(gl, renderer, mat);
}
} catch (GLException ex) {
LogBox.renderLogException(ex);
return; // The loader will detect that this did not load cleanly
}
_isLoadedGPU = true;
}
private void loadGPUMaterial(GL2GL3 gl, Renderer renderer, MeshData.Material dataMat) {
boolean hasTex = dataMat.colorTex != null;
Material mat = new Material();
mat.shaderID = dataMat.getShaderID();
mat._transType = dataMat.transType;
mat._transColour = dataMat.transColour;
if (hasTex) {
mat._texHandle = renderer.getTexCache().getTexID(gl, dataMat.colorTex, (dataMat.transType != MeshData.NO_TRANS), false, true);
} else {
mat._texHandle = 0;
mat._diffuseColor = new Color4d(dataMat.diffuseColor);
}
mat._ambientColor = new Color4d(dataMat.ambientColor);
mat._specColor = new Color4d(dataMat.specColor);
mat._shininess = dataMat.shininess;
_materials.add(mat);
}
public static void init(Renderer r, GL2GL3 gl) {
for (int i = 0; i < Renderer.NUM_MESH_SHADERS; ++i) {
ShaderInfo si = new ShaderInfo();
sInfos[i] = si;
si.meshProgHandle = r.getMeshShader(i).getProgramHandle();
gl.glUseProgram(si.meshProgHandle);
// Bind the shader variables
si.modelViewMatVar = gl.glGetUniformLocation(si.meshProgHandle, "modelViewMat");
si.projMatVar = gl.glGetUniformLocation(si.meshProgHandle, "projMat");
si.bindSpaceMatVar = gl.glGetUniformLocation(si.meshProgHandle, "bindSpaceMat");
si.bindSpaceNorMatVar = gl.glGetUniformLocation(si.meshProgHandle, "bindSpaceNorMat");
si.normalMatVar = gl.glGetUniformLocation(si.meshProgHandle, "normalMat");
si.diffuseColorVar = gl.glGetUniformLocation(si.meshProgHandle, "diffuseColor");
si.ambientColorVar = gl.glGetUniformLocation(si.meshProgHandle, "ambientColor");
si.specColorVar = gl.glGetUniformLocation(si.meshProgHandle, "specColor");
si.shininessVar = gl.glGetUniformLocation(si.meshProgHandle, "shininess");
si.texVar = gl.glGetUniformLocation(si.meshProgHandle, "diffuseTex");
si.lightDirVar = gl.glGetUniformLocation(si.meshProgHandle, "lightDir");
si.lightIntVar = gl.glGetUniformLocation(si.meshProgHandle, "lightIntensity");
si.numLightsVar = gl.glGetUniformLocation(si.meshProgHandle, "numLights");
si.cVar = gl.glGetUniformLocation(si.meshProgHandle, "C");
si.fcVar = gl.glGetUniformLocation(si.meshProgHandle, "FC");
}
numLights = 2;
lightsDir[0] = new Vec4d(-0.3, -0.2, -0.5, 0.0);
lightsDir[1] = new Vec4d( 0.5, 1.0, -0.1, 0.0);
lightsDir[0].normalize3();
lightsDir[1].normalize3();
lightsInt[0] = 1f;
lightsInt[1] = 0.5f;
lightsDirScratch[0] = new Vec4d();
lightsDirScratch[1] = new Vec4d();
}
private void loadGPUSubMesh(GL2GL3 gl, Renderer renderer, MeshData.SubMeshData data) {
boolean hasTex = data.texCoords != null;
SubMesh sub = new SubMesh(usedShaders);
int[] is = new int[3];
gl.glGenBuffers(3, is, 0);
sub._vertexBuffer = is[0];
sub._normalBuffer = is[1];
sub._indexBuffer = is[2];
if (hasTex) {
gl.glGenBuffers(1, is, 0);
sub._texCoordBuffer = is[0];
}
sub._center = data.staticHull.getAABBCenter();
sub._numVerts = data.indices.length;
if (flattenBuffers) {
FloatBuffer fb = FloatBuffer.allocate(data.indices.length * 3); //
for (int ind : data.indices) {
RenderUtils.putPointXYZ(fb, data.verts.get(ind));
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._vertexBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, data.indices.length * 3 * 4, fb, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(data.indices.length * 3 * 4);
} else
{
// Init vertices
FloatBuffer fb = FloatBuffer.allocate(data.verts.size() * 3); //
for (Vec3d v : data.verts) {
RenderUtils.putPointXYZ(fb, v);
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._vertexBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, data.verts.size() * 3 * 4, fb, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(data.verts.size() * 3 * 4);
}
// Init textureCoords
if (hasTex) {
if (flattenBuffers) {
FloatBuffer fb = FloatBuffer.allocate(data.indices.length * 2); //
for (int ind : data.indices) {
RenderUtils.putPointXY(fb, data.texCoords.get(ind));
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._texCoordBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, data.indices.length * 2 * 4, fb, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(data.indices.length * 2 * 4);
} else
{
FloatBuffer fb = FloatBuffer.allocate(data.texCoords.size() * 2); //
for (Vec2d v : data.texCoords) {
RenderUtils.putPointXY(fb, v);
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._texCoordBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, data.texCoords.size() * 2 * 4, fb, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(data.texCoords.size() * 2 * 4);
}
}
if (flattenBuffers) {
FloatBuffer fb = FloatBuffer.allocate(data.indices.length * 3); //
for (int ind : data.indices) {
RenderUtils.putPointXYZ(fb, data.normals.get(ind));
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._normalBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, data.indices.length * 3 * 4, fb, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(data.indices.length * 3 * 4);
} else
{
// Init normals
FloatBuffer fb = FloatBuffer.allocate(data.normals.size() * 3);
for (Vec3d v : data.normals) {
RenderUtils.putPointXYZ(fb, v);
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._normalBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, data.normals.size() * 3 * 4, fb, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(data.normals.size() * 3 * 4);
}
if (flattenBuffers) {
is[0] = sub._indexBuffer;
// Clear the unneeded buffer object
gl.glDeleteBuffers(1, is, 0);
} else
{
IntBuffer indexBuffer = IntBuffer.wrap(data.indices);
gl.glBindBuffer(GL2GL3.GL_ELEMENT_ARRAY_BUFFER, sub._indexBuffer);
gl.glBufferData(GL2GL3.GL_ELEMENT_ARRAY_BUFFER, sub._numVerts * 4, indexBuffer, GL2GL3.GL_STATIC_DRAW);
renderer.usingVRAM(sub._numVerts * 4);
}
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL2GL3.GL_ELEMENT_ARRAY_BUFFER, 0);
_subMeshes.add(sub);
// These will never be needed again, so let's just get rid of them
if (!data.keepRuntimeData) {
if (data.texCoords != null)
data.texCoords.clear();
if (data.normals != null)
data.normals.clear();
}
}
private void loadGPUSubLine(GL2GL3 gl, Renderer renderer, MeshData.SubLineData data) {
Shader s = renderer.getShader(Renderer.ShaderHandle.DEBUG);
assert (s.isGood());
SubLine sub = new SubLine();
sub._progHandle = s.getProgramHandle();
int[] is = new int[1];
gl.glGenBuffers(1, is, 0);
sub._vertexBuffer = is[0];
sub._numVerts = data.verts.size();
sub._hull = data.hull;
// Init vertices
FloatBuffer fb = FloatBuffer.allocate(data.verts.size() * 3); //
for (Vec3d v : data.verts) {
RenderUtils.putPointXYZ(fb, v);
}
fb.flip();
gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, sub._vertexBuffer);
gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, sub._numVerts * 3 * 4, fb, GL2GL3.GL_STATIC_DRAW);
// Bind the shader variables
sub._modelViewMatVar = gl.glGetUniformLocation(sub._progHandle, "modelViewMat");
sub._projMatVar = gl.glGetUniformLocation(sub._progHandle, "projMat");
sub._diffuseColor = new Color4d(data.diffuseColor);
sub._colorVar = gl.glGetUniformLocation(sub._progHandle, "color");
sub._cVar = gl.glGetUniformLocation(sub._progHandle, "C");
sub._fcVar = gl.glGetUniformLocation(sub._progHandle, "FC");
_subLines.add(sub);
}
/**
* Has this mesh been loaded into GPU memory?
* @return
*/
public boolean isLoadedGPU() {
return _isLoadedGPU;
}
public void freeResources(GL2GL3 gl) {
for (SubMesh sub : _subMeshes) {
int[] bufs = new int[6];
bufs[0] = sub._vertexBuffer;
bufs[1] = sub._normalBuffer;
bufs[2] = sub._texCoordBuffer;
bufs[3] = sub._indexBuffer;
gl.glDeleteBuffers(4, bufs, 0);
}
for (SubLine sub : _subLines) {
int[] bufs = new int[1];
bufs[0] = sub._vertexBuffer;
gl.glDeleteBuffers(1, bufs, 0);
}
_subMeshes.clear();
}
public MeshData.Pose getPose(ArrayList<Action.Queue> actions) {
return data.getPose(actions);
}
public ConvexHull getHull(MeshData.Pose pose) {
return data.getHull(pose);
}
public boolean hasTransparent() {
return data.hasTransparent();
}
public MeshData getRawData() {
return data;
}
} // class MeshProto