/* * 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.nio.FloatBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import com.jaamsim.math.AABB; import com.jaamsim.math.Color4d; import com.jaamsim.math.Mat4d; import com.jaamsim.math.MathUtils; import com.jaamsim.math.Ray; import com.jaamsim.math.Transform; import com.jaamsim.math.Vec3d; import com.jaamsim.math.Vec4d; import com.jaamsim.render.Renderer.ShaderHandle; import com.jogamp.opengl.GL2GL3; /** * A Renderable that draws convex (or relatively convex) polygons. Specifically it draw a triangle fan through all the * provided points centered at the median point * * @author matt.chudleigh * */ public class Polygon implements Renderable { private static int _vertBuffer; private static int _progHandle; // The flat, unshaded program handle private static int _modelViewMatVar; private static int _projMatVar; private static int _colorVar; private static int _posVar; private static int _cVar; private static int _fcVar; private static HashMap<Integer, Integer> _VAOMap = new HashMap<>(); private static boolean _hasInitialized; private final ArrayList<Vec3d> _points; private final VisibilityInfo _visInfo; private final float[] colour; private final float[] hoverColour; private final boolean isOutline; private final double lineWidth; // only meaningful if (isOutline) private final long pickingID; private final Transform trans; private final AABB _bounds; FloatBuffer fb; public Polygon(List<Vec4d> points, Transform trans, Vec3d scale, Color4d colour, Color4d hoverColour, VisibilityInfo visInfo, boolean isOutline, double lineWidth, long pickingID) { this.colour = colour.toFloats(); this.hoverColour = hoverColour.toFloats(); this.isOutline = isOutline; this.lineWidth = lineWidth; this.pickingID = pickingID; this.trans = trans; this._visInfo = visInfo; // Points includes the scale, but not the transform _points = new ArrayList<>(points.size()); ArrayList<Vec3d> boundsPoints = new ArrayList<>(points.size()); for (Vec4d p : points) { Vec3d temp = new Vec3d(p); temp.mul3(scale); _points.add(temp); Vec3d bTemp = new Vec3d(); trans.multAndTrans(temp, bTemp); boundsPoints.add(bTemp); } _bounds = new AABB(boundsPoints); if (this.isOutline) { fb = FloatBuffer.allocate(3 * _points.size()); for (Vec3d vert : _points) { RenderUtils.putPointXYZ(fb, vert); } } else { // Otherwise make a triangle fan c Vec3d center = new Vec3d(); for (Vec3d vert : _points) { center.add3(vert); } center.scale3(1.0/_points.size()); // The vertex list is just the closed loop of points int buffSize = 3 * (_points.size() + 2); fb = FloatBuffer.allocate(buffSize); // Put the center to start the triangle fan RenderUtils.putPointXYZ(fb, center); for (Vec3d vert : _points) { RenderUtils.putPointXYZ(fb, vert); } RenderUtils.putPointXYZ(fb, _points.get(0)); } fb.flip(); } @Override public void render(int contextID, Renderer renderer, Camera cam, Ray pickRay) { assert(_hasInitialized); GL2GL3 gl = renderer.getGL(); if (_points.size() < 3) { return; // Can't actually draw this polygon } float[] renderColour = colour; if (pickRay != null && getCollisionDist(pickRay, false) > 0) renderColour = hoverColour; if (renderColour[3] != 1.0) { // Transparent, render this on the transparent pass instead return; } renderImp(contextID, renderer, cam, pickRay, gl, renderColour); } private void renderImp(int contextID, Renderer renderer, Camera cam, Ray pickRay, GL2GL3 gl, float[] renderColour) { if (!_VAOMap.containsKey(contextID)) { setupVAO(contextID, renderer); } int vao = _VAOMap.get(contextID); gl.glBindVertexArray(vao); gl.glUseProgram(_progHandle); // Setup uniforms for this object Mat4d projMat = cam.getProjMat4d(); Mat4d modelViewMat = new Mat4d(); cam.getViewMat4d(modelViewMat); modelViewMat.mult4(trans.getMat4dRef()); gl.glUniformMatrix4fv(_modelViewMatVar, 1, false, RenderUtils.MarshalMat4d(modelViewMat), 0); gl.glUniformMatrix4fv(_projMatVar, 1, false, RenderUtils.MarshalMat4d(projMat), 0); gl.glUniform1f(_cVar, Camera.C); gl.glUniform1f(_fcVar, Camera.FC); gl.glUniform4fv(_colorVar, 1, renderColour, 0); if (isOutline) { renderOutline(gl); } else { renderFill(gl); } gl.glBindVertexArray(0); } private void renderOutline(GL2GL3 gl) { // The vertex list is just the closed loop of points if (lineWidth == 0) { return; } gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, _vertBuffer); gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, fb.limit() * 4, fb, GL2GL3.GL_STATIC_DRAW); gl.glVertexAttribPointer(_posVar, 3, GL2GL3.GL_FLOAT, false, 0, 0); gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, 0); if (!gl.isGLcore()) gl.glLineWidth((float)lineWidth); else gl.glLineWidth(1.0f); gl.glDrawArrays(GL2GL3.GL_LINE_LOOP, 0, _points.size()); gl.glLineWidth(1.0f); } private void renderFill(GL2GL3 gl) { gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, _vertBuffer); gl.glBufferData(GL2GL3.GL_ARRAY_BUFFER, fb.limit() * 4, fb, GL2GL3.GL_STATIC_DRAW); gl.glVertexAttribPointer(_posVar, 3, GL2GL3.GL_FLOAT, false, 0, 0); gl.glBindBuffer(GL2GL3.GL_ARRAY_BUFFER, 0); gl.glDrawArrays(GL2GL3.GL_TRIANGLE_FAN, 0, _points.size() + 2); } @Override public long getPickingID() { return pickingID; } @Override public AABB getBoundsRef() { return _bounds; } @Override public double getCollisionDist(Ray r, boolean precise) { double dist = _bounds.collisionDist(r); if (dist < 0) { return dist; } // Does not collide with the bounds // Create a ray in the points coordinate space Transform invTrans = new Transform(); trans.inverse(invTrans); Ray localRay = r.transform(invTrans); return MathUtils.collisionDistPoly(localRay, _points); } // This should be called from the renderer at initialization public static void init(Renderer r, GL2GL3 gl) { int[] is = new int[1]; gl.glGenBuffers(1, is, 0); _vertBuffer = is[0]; Shader s = r.getShader(ShaderHandle.DEBUG); _progHandle = s.getProgramHandle(); gl.glUseProgram(_progHandle); _modelViewMatVar = gl.glGetUniformLocation(_progHandle, "modelViewMat"); _projMatVar = gl.glGetUniformLocation(_progHandle, "projMat"); _colorVar = gl.glGetUniformLocation(_progHandle, "color"); _cVar = gl.glGetUniformLocation(_progHandle, "C"); _fcVar = gl.glGetUniformLocation(_progHandle, "FC"); _posVar = gl.glGetAttribLocation(_progHandle, "position"); _hasInitialized = true; } private static void setupVAO(int contextID, Renderer renderer) { GL2GL3 gl = renderer.getGL(); int vao = renderer.generateVAO(contextID, gl); _VAOMap.put(contextID, vao); gl.glBindVertexArray(vao); gl.glUseProgram(_progHandle); gl.glEnableVertexAttribArray(_posVar); gl.glBindVertexArray(0); } @Override public boolean hasTransparent() { // If either the colour, or hover colour have transparent parts, this could be tranparent return (colour[3] != 1.0) || (hoverColour[3] != 1.0); } @Override public void renderTransparent(int contextID, Renderer renderer, Camera cam, Ray pickRay) { GL2GL3 gl = renderer.getGL(); if (_points.size() < 3) { return; // Can't actually draw this polygon } float[] renderColour = colour; if (pickRay != null && getCollisionDist(pickRay, false) > 0) renderColour = hoverColour; if (renderColour[3] == 1.0) { // This is opaque return; } renderImp(contextID, renderer, cam, pickRay, gl, renderColour); } @Override public boolean renderForView(int viewID, Camera cam) { double dist = cam.distToBounds(getBoundsRef()); return _visInfo.isVisible(viewID, dist); } }