/** * Copyright 2012 Jason Sorensen (sorensenj@smert.net) * * 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 net.smert.frameworkgl; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import net.smert.frameworkgl.gameobjects.GameObject; import net.smert.frameworkgl.math.AABB; import net.smert.frameworkgl.math.AABBUtilities; import net.smert.frameworkgl.math.Transform4f; import net.smert.frameworkgl.math.Vector3f; import net.smert.frameworkgl.opengl.GL; import net.smert.frameworkgl.opengl.MaterialLight; import net.smert.frameworkgl.opengl.Shader; import net.smert.frameworkgl.opengl.Texture; import net.smert.frameworkgl.opengl.camera.Camera; import net.smert.frameworkgl.opengl.constants.ShaderTypes; import net.smert.frameworkgl.opengl.constants.TextureTargets; import net.smert.frameworkgl.opengl.font.AwtFont; import net.smert.frameworkgl.opengl.image.ImageReader; import net.smert.frameworkgl.opengl.mesh.Mesh; import net.smert.frameworkgl.opengl.mesh.Segment; import net.smert.frameworkgl.opengl.mesh.Tessellator; import net.smert.frameworkgl.opengl.renderable.AbstractRenderable; import net.smert.frameworkgl.opengl.renderable.Renderable; import net.smert.frameworkgl.opengl.renderable.RenderableConfiguration; import net.smert.frameworkgl.opengl.renderable.factory.RenderableFactory; import net.smert.frameworkgl.opengl.renderable.shared.DrawCommands; import net.smert.frameworkgl.opengl.renderer.AbstractRendererGL; import net.smert.frameworkgl.opengl.renderer.AwtFontRenderer; import net.smert.frameworkgl.opengl.renderer.FontRenderer; import net.smert.frameworkgl.opengl.renderer.GLRenderer; import net.smert.frameworkgl.opengl.shader.AbstractShader; /** * * @author Jason Sorensen <sorensenj@smert.net> */ public class Graphics implements GLRenderer { private static RenderableComparison renderableComparison; private int openglMajorVersion; private AbstractRendererGL renderer; private FontRenderer fontRenderer; private RenderableFactory renderableFactory; public Graphics() { renderableComparison = new RenderableComparison(); openglMajorVersion = 1; } public Shader buildShader(String fragmentShaderFilename, String vertexShaderFilename, String shaderName) throws IOException { return GL.shaderBuilder. load(fragmentShaderFilename, ShaderTypes.FRAGMENT_SHADER). load(vertexShaderFilename, ShaderTypes.VERTEX_SHADER). compileShaders(). buildProgram(shaderName). createShader(true); } public AbstractRenderable createArrayRenderable() { return renderableFactory.createArrayRenderable(); } public AbstractRenderable createDisplayListRenderable() { return renderableFactory.createDisplayListRenderable(); } public AbstractRenderable createDynamicInterleavedRenderable() { return renderableFactory.createDynamicInterleavedRenderable(); } public AbstractRenderable createDynamicNonInterleavedRenderable() { return renderableFactory.createDynamicNonInterleavedRenderable(); } public AbstractRenderable createImmediateModeRenderable() { return renderableFactory.createImmediateModeRenderable(); } public AbstractRenderable createInterleavedRenderable() { return renderableFactory.createInterleavedRenderable(); } public Mesh createMesh(DrawCommands drawCommands) { // Create new segment with the draw commands Segment segment = GL.meshFactory.createSegment(); segment.setDrawCommands(drawCommands); // Check to see if a renderable configuration exists before adding it RenderableConfiguration config = GL.meshFactory.createRenderableConfiguration(); int renderableConfigID = Renderable.configPool.getOrAdd(config); // Create mesh and set config ID and add segment Mesh mesh = GL.meshFactory.createMesh(); mesh.setRenderableConfigID(renderableConfigID); mesh.addSegment(segment); return mesh; } public Mesh createMesh(Tessellator tessellator) { Mesh mesh = GL.meshFactory.createMesh(); return createMesh(tessellator, mesh); } public Mesh createMesh(Tessellator tessellator, Mesh mesh) { // Reset the mesh mesh.reset(); // Save AABB tessellator.getAABB(mesh.getAabb()); // Check to see if a renderable configuration exists before adding it RenderableConfiguration config = tessellator.getRenderableConfiguration(); int renderableConfigID = Renderable.configPool.getOrAdd(config); // Set config ID mesh.setRenderableConfigID(renderableConfigID); // Add segments List<Segment> segments = tessellator.getSegments(); for (Segment segment : segments) { mesh.addSegment(segment); } return mesh; } public AbstractRenderable createNonInterleavedRenderable() { return renderableFactory.createNonInterleavedRenderable(); } /** * This method should be called just before the Display is destroyed. This is automatically called in Application * during the normal shutdown process. */ public void destroy() { GL.renderer1.destroy(); GL.renderer2.destroy(); GL.renderer3.destroy(); Renderable.bindState.reset(); Renderable.shaderBindState.reset(); Renderable.textureBindState.reset(); GL.fboHelper.unbind(); GL.textureHelper.unbind(); GL.vboHelper.unbind(); Renderable.configPool.destroy(); Renderable.materialLightPool.destroy(); Renderable.shaderPool.destroy(); Renderable.texturePool.destroy(); } public int getOpenglMajorVersion() { return openglMajorVersion; } public AbstractRendererGL getRenderer() { return renderer; } public Comparator<GameObject> getRenderableComparison() { return renderableComparison; } public void setRenderableComparison(RenderableComparison renderableComparison) { Graphics.renderableComparison = renderableComparison; } public FontRenderer getFontRenderer() { return fontRenderer; } public void setFontRenderer(FontRenderer fontRenderer) { this.fontRenderer = fontRenderer; } public Texture getTexture(String filename) { return Renderable.texturePool.get(filename); } public void init() { MaterialLight materialLight = GL.glFactory.createMaterialLight(); GL.renderer1.init(); GL.renderer2.init(); GL.renderer3.init(); GL.uniformVariables.setAmbientLight(GL.glFactory.createAmbientLight()); GL.uniformVariables.setDefaultMaterialLight(materialLight); GL.uniformVariables.setGlLight(GL.glFactory.createGLLight()); GL.uniformVariables.setGlLights(new ArrayList<>()); Renderable.bindState.setAttribLocations(GL.defaultAttribLocations); Renderable.materialLightPool.add("default", materialLight); Renderable.shaderBindState.init(); AwtFont awtFont = GL.awtFontBuilder. addUsAsciiGlyphs(). setAntiAliasing(true). setBold(true). setFamily("Dialog"). setLeftToRight(true). setSize(16). buildFont(). createFont(true); AwtFontRenderer awtFontRenderer = GL.rendererFactory.createAwtFontRenderer(); awtFontRenderer.init(awtFont); setFontRenderer(awtFontRenderer); switchOpenGLVersion(1); // Switch to OpenGL 1.X } public void loadMesh(String filename, Mesh mesh) throws IOException { GL.meshReader.load(filename, mesh); } public void loadCubeMapTexture(String folderName, String fileExtension) throws IOException { ImageReader imageReader = GL.textureReader.getImageReader("." + fileExtension); String filename; filename = folderName + "/xpos." + fileExtension; BufferedImage xPosBufferedImage = imageReader.load(filename); filename = folderName + "/xneg." + fileExtension; BufferedImage xNegBufferedImage = imageReader.load(filename); filename = folderName + "/ypos." + fileExtension; BufferedImage yPosBufferedImage = imageReader.load(filename); filename = folderName + "/yneg." + fileExtension; BufferedImage yNegBufferedImage = imageReader.load(filename); filename = folderName + "/zpos." + fileExtension; BufferedImage zPosBufferedImage = imageReader.load(filename); filename = folderName + "/zneg." + fileExtension; BufferedImage zNegBufferedImage = imageReader.load(filename); GL.textureBuilder. setLoadFlipVertically(false) .loadCube(xPosBufferedImage, TextureTargets.TEXTURE_CUBE_MAP_POSITIVE_X) .loadCube(xNegBufferedImage, TextureTargets.TEXTURE_CUBE_MAP_NEGATIVE_X) .loadCube(yPosBufferedImage, TextureTargets.TEXTURE_CUBE_MAP_POSITIVE_Y) .loadCube(yNegBufferedImage, TextureTargets.TEXTURE_CUBE_MAP_NEGATIVE_Y) .loadCube(zPosBufferedImage, TextureTargets.TEXTURE_CUBE_MAP_POSITIVE_Z) .loadCube(zNegBufferedImage, TextureTargets.TEXTURE_CUBE_MAP_NEGATIVE_Z) .setClampingWrapRClampToEdge().setClampingWrapSClampToEdge().setClampingWrapTClampToEdge() .setFilterMagLinear().setFilterMinLinear().setTextureTargetCubeMap().buildTexture(); Texture texture = GL.textureBuilder.createTexture(true); // Remove texture from pool String textureFilename = folderName + "/cubemap." + fileExtension; Texture existingTexture = Renderable.texturePool.remove(textureFilename); if (existingTexture != null) { existingTexture.destroy(); } // Add texture to pool Renderable.texturePool.add(textureFilename, texture); } public void loadTexture(String filename) throws IOException { Texture texture = GL.textureReader.load(filename); // Remove texture from pool Texture existingTexture = Renderable.texturePool.remove(filename); if (existingTexture != null) { existingTexture.destroy(); } // Add texture to pool Renderable.texturePool.add(filename, texture); } public void loadTextures(Mesh mesh) throws IOException { List<String> textures = mesh.getTextures(); for (String filename : textures) { Texture texture = GL.textureReader.load(filename); // Remove texture from pool Texture existingTexture = Renderable.texturePool.remove(filename); if (existingTexture != null) { existingTexture.destroy(); } // Add texture to pool Renderable.texturePool.add(filename, texture); } } public void performCulling(Camera camera, GameObject gameObject) { boolean inFrustum = camera.getFrustumCulling().isAABBInFrustum(gameObject.getWorldAabb()); gameObject.getRenderableState().setInFrustum(inFrustum); } public void performCulling(Camera camera, List<GameObject> gameObjects) { for (GameObject gameObject : gameObjects) { performCulling(camera, gameObject); } } public void setDefaultFontRenderer(FontRenderer defaultFontRenderer) { renderer.setDefaultFontRenderer(defaultFontRenderer); } public void sort(List<GameObject> gameObjects, Vector3f cameraPosition) { sort(gameObjects, cameraPosition, renderableComparison); } public void sort(List<GameObject> gameObjects, Vector3f cameraPosition, RenderableComparison renderableComparison) { renderableComparison.setCameraPosition(cameraPosition); Collections.sort(gameObjects, renderableComparison); } public void switchOpenGLVersion(int openglMajorVersion) { this.openglMajorVersion = openglMajorVersion; switch (this.openglMajorVersion) { case 1: renderableFactory = GL.rf1; renderer = GL.renderer1; break; case 2: renderableFactory = GL.rf2; renderer = GL.renderer2; break; case 3: renderableFactory = GL.rf3; renderer = GL.renderer3; break; default: throw new RuntimeException("Unknown OpenGL version: " + openglMajorVersion); } setDefaultFontRenderer(fontRenderer); } public void updateAabb(GameObject gameObject) { AABB localAabb = gameObject.getMesh().getAabb(); AABB worldAabb = gameObject.getWorldAabb(); Transform4f worldTransform = gameObject.getWorldTransform(); AABBUtilities.Transform(localAabb, worldTransform, worldAabb); } public void updateAabb(GameObject gameObject, float margin) { AABB localAabb = gameObject.getMesh().getAabb(); AABB worldAabb = gameObject.getWorldAabb(); Transform4f worldTransform = gameObject.getWorldTransform(); AABBUtilities.Transform(localAabb, margin, worldTransform, worldAabb); } public void updateAabb(List<GameObject> gameObjects) { for (GameObject gameObject : gameObjects) { updateAabb(gameObject); } } public void updateAabb(List<GameObject> gameObjects, float margin) { for (GameObject gameObject : gameObjects) { updateAabb(gameObject, margin); } } @Override public void color(float r, float g, float b, float a) { renderer.color(r, g, b, a); } @Override public void disableTexture2D() { renderer.disableTexture2D(); } @Override public void disableTexture3D() { renderer.disableTexture3D(); } @Override public void disableTextureCubeMap() { renderer.disableTextureCubeMap(); } @Override public void enableTexture2D() { renderer.enableTexture2D(); } @Override public void enableTexture3D() { renderer.enableTexture3D(); } @Override public void enableTextureCubeMap() { renderer.enableTextureCubeMap(); } @Override public void popMatrix() { renderer.popMatrix(); } @Override public void pushMatrix() { renderer.pushMatrix(); } @Override public void render(AbstractRenderable renderable) { renderer.render(renderable); } @Override public void render(AbstractRenderable renderable, float x, float y, float z) { renderer.render(renderable, x, y, z); } @Override public void render(AbstractRenderable renderable, Transform4f transform) { renderer.render(renderable, transform); } @Override public void render(AbstractRenderable renderable, Vector3f position) { renderer.render(renderable, position); } @Override public void render(GameObject gameObject) { renderer.render(gameObject); } @Override public void render(List<GameObject> gameObjects) { renderer.render(gameObjects); } @Override public void renderBlend(GameObject gameObject) { renderer.renderBlend(gameObject); } @Override public void renderBlend(List<GameObject> gameObjects) { renderer.renderBlend(gameObjects); } @Override public void renderOpaque(GameObject gameObject) { renderer.renderOpaque(gameObject); } @Override public void renderOpaque(List<GameObject> gameObjects) { renderer.renderOpaque(gameObjects); } @Override public void scale(float x, float y, float z) { renderer.scale(x, y, z); } @Override public void scale(Vector3f scaling) { renderer.scale(scaling); } @Override public void set2DMode() { renderer.set2DMode(); } @Override public void set2DMode(int width, int height) { renderer.set2DMode(width, height); } @Override public void setCamera(Camera camera) { renderableComparison.setCameraPosition(camera.getPosition()); renderer.setCamera(camera); } @Override public void switchShader(AbstractShader shader) { renderer.switchShader(shader); } @Override public void translate(float x, float y, float z) { renderer.translate(x, y, z); } @Override public void translate(Vector3f position) { renderer.translate(position); } @Override public void unbindShader() { renderer.unbindShader(); } public static class RenderableComparison implements Comparator<GameObject> { private Vector3f cameraPosition; public void setCameraPosition(Vector3f cameraPosition) { this.cameraPosition = cameraPosition; } @Override public int compare(GameObject o1, GameObject o2) { boolean o1Opaque = o1.getRenderableState().isOpaque(); boolean o2Opaque = o2.getRenderableState().isOpaque(); if ((o1Opaque == o2Opaque) && o1Opaque) { return 0; } if ((o1Opaque != o2Opaque) && !o1Opaque) { return 1; } if ((o1Opaque != o2Opaque) && o1Opaque) { return -1; } // Both objects are not opaque Vector3f o1Position = o1.getWorldTransform().getPosition(); Vector3f o2Position = o2.getWorldTransform().getPosition(); return (int) cameraPosition.distanceSquared(o2Position) - (int) cameraPosition.distanceSquared(o1Position); } } }