Add files via upload

This commit is contained in:
TheThinMatrix 2018-01-22 16:23:05 +01:00 committed by GitHub
parent 267883fb07
commit 032471dda2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 422 additions and 0 deletions

View File

@ -0,0 +1,56 @@
package engineTester;
import org.lwjgl.opengl.Display;
import renderEngine.DisplayManager;
import renderEngine.Loader;
import renderEngine.RawModel;
import renderEngine.Renderer;
/**
* This class contains the main method and is used to test the engine.
*
* @author Karl
*
*/
public class MainGameLoop {
/**
* Loads up the position data for two triangles (which together make a quad)
* into a VAO. This VAO is then rendered to the screen every frame.
*
* @param args
*/
public static void main(String[] args) {
DisplayManager.createDisplay();
Loader loader = new Loader();
Renderer renderer = new Renderer();
float[] vertices = {
-0.5f, 0.5f, 0f,//v0
-0.5f, -0.5f, 0f,//v1
0.5f, -0.5f, 0f,//v2
0.5f, 0.5f, 0f,//v3
};
int[] indices = {
0,1,3,//top left triangle (v0, v1, v3)
3,1,2//bottom right triangle (v3, v1, v2)
};
RawModel model = loader.loadToVAO(vertices, indices);
while (!Display.isCloseRequested()) {
// game logic
renderer.prepare();
renderer.render(model);
DisplayManager.updateDisplay();
}
loader.cleanUp();
DisplayManager.closeDisplay();
}
}

View File

@ -0,0 +1,60 @@
package renderEngine;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.PixelFormat;
/**
* This class contains all the methods needed to set-up, maintain, and close a LWJGL display.
*
* @author Karl
*
*/
public class DisplayManager {
private static final int WIDTH = 1280;
private static final int HEIGHT = 720;
private static final int FPS_CAP = 60;
private static final String TITLE = "Our First Display";
/**
* Creates a display window on which we can render our game. The dimensions
* of the window are determined by setting the display mode. By using
* "glViewport" we tell OpenGL which part of the window we want to render
* our game onto. We indicated that we want to use the entire window.
*/
public static void createDisplay() {
ContextAttribs attribs = new ContextAttribs(3, 2).withForwardCompatible(true).withProfileCore(true);
try {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.create(new PixelFormat(), attribs);
Display.setTitle(TITLE);
} catch (LWJGLException e) {
e.printStackTrace();
}
GL11.glViewport(0, 0, WIDTH, HEIGHT);
}
/**
* This method is used to update the display at the end of every frame. When
* we have set up a rendering process this method will display whatever
* we've been rendering onto the screen. The "sync" method is used here to
* cap the frame rate. Without this the computer would just try to run the
* game as fast as it possibly can, doing more work than it needs to.
*/
public static void updateDisplay() {
Display.sync(FPS_CAP);
Display.update();
}
/**
* This closes the window when the game is closed.
*/
public static void closeDisplay() {
Display.destroy();
}
}

View File

@ -0,0 +1,213 @@
package renderEngine;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
/**
* Handles the loading of geometry data into VAOs. It also keeps track of all
* the created VAOs and VBOs so that they can all be deleted when the game
* closes.
*
* @author Karl
*
*/
public class Loader {
private List<Integer> vaos = new ArrayList<Integer>();
private List<Integer> vbos = new ArrayList<Integer>();
/**
* Creates a VAO and stores the position data of the vertices into attribute
* 0 of the VAO. The indices are stored in an index buffer and bound to the
* VAO.
*
* @param positions
* - The 3D positions of each vertex in the geometry (in this
* example a quad).
* @param indices
* - The indices of the model that we want to store in the VAO.
* The indices indicate how the vertices should be connected
* together to form triangles.
* @return The loaded model.
*/
public RawModel loadToVAO(float[] positions, int[] indices) {
int vaoID = createVAO();
bindIndicesBuffer(indices);
storeDataInAttributeList(0, positions);
unbindVAO();
return new RawModel(vaoID, indices.length);
}
/**
* Deletes all the VAOs and VBOs when the game is closed. VAOs and VBOs are
* located in video memory.
*/
public void cleanUp() {
for (int vao : vaos) {
GL30.glDeleteVertexArrays(vao);
}
for (int vbo : vbos) {
GL15.glDeleteBuffers(vbo);
}
}
/**
* Creates a new VAO and returns its ID. A VAO holds geometry data that we
* can render and is physically stored in memory on the GPU, so that it can
* be accessed very quickly during rendering.
*
* Like most objects in OpenGL, the new VAO is created using a "gen" method
* which returns the ID of the new VAO. In order to use the VAO it needs to
* be made the active VAO. Only one VAO can be active at a time. To make
* this VAO the active VAO (so that we can store stuff in it) we have to
* bind it.
*
* @return The ID of the newly created VAO.
*/
private int createVAO() {
int vaoID = GL30.glGenVertexArrays();
vaos.add(vaoID);
GL30.glBindVertexArray(vaoID);
return vaoID;
}
/**
* Stores the position data of the vertices into attribute 0 of the VAO. To
* do this the positions must first be stored in a VBO. You can simply think
* of a VBO as an array of data that is stored in memory on the GPU for easy
* access during rendering.
*
* Just like with the VAO, we create a new VBO using a "gen" method, and
* make it the active VBO (so that we do stuff to it) by binding it.
*
* We then store the positions data in the active VBO by using the
* glBufferData method. We also indicate using GL_STATIC_DRAW that this data
* won't need to be changed. If we wanted to edit the positions every frame
* (perhaps to animate the quad) then we would use GL_DYNAMIC_DRAW instead.
*
* We the connect the VBO to the VAO using the glVertexAttribPointer()
* method. This needs to know the attribute number of the VAO where we want
* to put the data, the number of floats used for each vertex (3 floats in
* this case, because each vertex has a 3D position, an x, y, and z value),
* the type of data (in this case we used floats) and then some other more
* complicated stuff for storing the data in more fancy ways. Don't worry
* about the last 3 parameters for now, we don't need them here.
*
* Now that we've finished using the VBO we can unbind it. This isn't
* totally necessary, but I think it's good practice to unbind the VBO when
* you're done using it.
*
* @param attributeNumber
* - The number of the attribute of the VAO where the data is to
* be stored.
* @param data
* - The geometry data to be stored in the VAO, in this case the
* positions of the vertices.
*/
private void storeDataInAttributeList(int attributeNumber, float[] data) {
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
FloatBuffer buffer = storeDataInFloatBuffer(data);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(attributeNumber, 3, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
/**
* Unbinds the VAO after we're finished using it. If we want to edit or use
* the VAO we would have to bind it again first.
*/
private void unbindVAO() {
GL30.glBindVertexArray(0);
}
/**
* Creates an index buffer, binds the index buffer to the currently active
* VAO, and then fills it with our indices.
*
* The index buffer is different from other data that we might store in the
* attributes of the VAO. When we stored the positions we were storing data
* about each vertex. The positions were "attributes" of each vertex. Data
* like that is stored in an attribute list of the VAO.
*
* The index buffer however does not contain data about each vertex. Instead
* it tells OpenGL how the vertices should be connected. Each VAO can only
* have one index buffer associated with it. This is why we don't store the
* index buffer in a certain attribute of the VAO; each VAO has one special
* "slot" for an index buffer and simply binding the index buffer binds it
* to the currently active VAO. When the VAO is rendered it will use the
* index buffer that is bound to it.
*
* This is also why we don't unbind the index buffer, as that would unbind
* it from the VAO.
*
* Note that we tell OpenGL that this is an index buffer by using
* "GL_ELEMENT_ARRAY_BUFFER" instead of "GL_ARRAY_BUFFER". This is how
* OpenGL knows to bind it as the index buffer for the current VAO.
*
* @param indices
*/
private void bindIndicesBuffer(int[] indices) {
int vboId = GL15.glGenBuffers();
vbos.add(vboId);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboId);
IntBuffer buffer = storeDataInIntBuffer(indices);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
}
/**
* Converts the indices from an int array to an IntBuffer so that they can
* be stored in a VBO. Very similar to the storeDataInFloatBuffer() method
* below.
*
* @param data
* - The indices in an int[].
* @return The indices in a buffer.
*/
private IntBuffer storeDataInIntBuffer(int[] data) {
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
/**
* Before we can store data in a VBO it needs to be in a certain format: in
* a buffer. In this case we will use a float buffer because the data we
* want to store is float data. If we were storing int data we would use an
* IntBuffer.
*
* First and empty buffer of the correct size is created. You can think of a
* buffer as basically an array with a pointer. After putting the necessary
* data into the buffer the pointer will have increased so that it points at
* the first empty element of the array. This is so that we could add more
* data to the buffer if we wanted and it wouldn't overwrite the data we've
* already put in. However, we're done with storing data and we want to make
* the buffer ready for reading. To do this we need to make the pointer
* point to the start of the data, so that OpenGL knows where in the buffer
* to start reading. The "flip()" method does just that, putting the pointer
* back to the start of the buffer.
*
* @param data
* - The float data that is going to be stored in the buffer.
* @return The FloatBuffer containing the data. This float buffer is ready
* to be loaded into a VBO.
*/
private FloatBuffer storeDataInFloatBuffer(float[] data) {
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}

View File

@ -0,0 +1,35 @@
package renderEngine;
/**
* Represents a loaded model. It contains the ID of the VAO that contains the
* model's data, and holds the number of vertices in the model.
*
* @author Karl
*
*/
public class RawModel {
private int vaoID;
private int vertexCount;
public RawModel(int vaoID, int vertexCount) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
}
/**
* @return The ID of the VAO which contains the data about all the geometry
* of this model.
*/
public int getVaoID() {
return vaoID;
}
/**
* @return The number of vertices in the model.
*/
public int getVertexCount() {
return vertexCount;
}
}

View File

@ -0,0 +1,58 @@
package renderEngine;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
/**
* Handles the rendering of a model to the screen.
*
* @author Karl
*
*/
public class Renderer {
/**
* This method must be called each frame, before any rendering is carried
* out. It basically clears the screen of everything that was rendered last
* frame (using the glClear() method). The glClearColor() method determines
* the colour that it uses to clear the screen. In this example it makes the
* entire screen red at the start of each frame.
*/
public void prepare() {
GL11.glClearColor(1, 0, 0, 1);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
}
/**
* Renders a model to the screen.
*
* Before we can render a VAO it needs to be made active, and we can do this
* by binding it. We also need to enable the relevant attributes of the VAO,
* which in this case is just attribute 0 where we stored the position data.
*
* The VAO can then be rendered to the screen using glDrawElements(). Using
* this draw method tells OpenGL that we want to use the index buffer to
* determine how the vertices should be connected instead of just connecting
* the vertices together in the order that they are stored in the VAO.
*
* We tell it what type of shapes to render and the number of vertices that
* it needs to render. We also tell it was format the index data is in (we
* used ints) and finally we indicate where in the index buffer it should
* start rendering. We want it to start right at the beginning and render
* everything, so we put 0.
*
* After rendering we unbind the VAO and disable the attribute.
*
* @param model
* - The model to be rendered.
*/
public void render(RawModel model) {
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
}