Ajout de la classe:
Scene, VertexArray, Shader, ShaderUtils, BufferUtils, FileUtils, Matrix4f, Vector3f Ajout des shaders de test: vert.vert et frag.frag dans le dossier shaders
This commit is contained in:
parent
4e88d1d1a4
commit
b02119dca7
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
||||
/.idea/
|
||||
/.gitignore
|
||||
/jeu-de-combat.iml
|
||||
/out/
|
||||
/lwjgl-release-3.2.3-custom/
|
||||
|
BIN
res/bg.jpeg
BIN
res/bg.jpeg
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
BIN
res/bird.png
BIN
res/bird.png
Binary file not shown.
Before Width: | Height: | Size: 64 KiB |
BIN
res/pipe.png
BIN
res/pipe.png
Binary file not shown.
Before Width: | Height: | Size: 68 KiB |
@ -1,14 +0,0 @@
|
||||
# version 330 core
|
||||
|
||||
layout (location = 0) out vec4 color;
|
||||
|
||||
in DATA {
|
||||
vec2 tc;
|
||||
} fs_in;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(tex, fs_in.tc);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
# version 330 core
|
||||
|
||||
layout (location = 0) in vec4 position;
|
||||
layout (location = 2) in vec2 tc;
|
||||
|
||||
uniform mat4 pr_matrix;
|
||||
|
||||
out DATA {
|
||||
vec2 tc;
|
||||
} vs_out;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = pr_matrix * position;
|
||||
vs_out.tc = tc;
|
||||
}
|
7
shaders/frag.frag
Normal file
7
shaders/frag.frag
Normal file
@ -0,0 +1,7 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
|
||||
}
|
7
shaders/vert.vert
Normal file
7
shaders/vert.vert
Normal file
@ -0,0 +1,7 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
import org.lwjgl.*;
|
||||
import org.lwjgl.glfw.*;
|
||||
import org.lwjgl.opengl.*;
|
||||
import org.lwjgl.system.*;
|
||||
|
||||
import java.nio.*;
|
||||
|
||||
import static org.lwjgl.glfw.Callbacks.*;
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.system.MemoryStack.*;
|
||||
import static org.lwjgl.system.MemoryUtil.*;
|
||||
|
||||
public class HelloWorld {
|
||||
|
||||
// The window handle
|
||||
private long window;
|
||||
|
||||
public void run() {
|
||||
System.out.println("Hello LWJGL " + Version.getVersion() + "!");
|
||||
|
||||
init();
|
||||
loop();
|
||||
|
||||
// Free the window callbacks and destroy the window
|
||||
glfwFreeCallbacks(window);
|
||||
glfwDestroyWindow(window);
|
||||
|
||||
// Terminate GLFW and free the error callback
|
||||
glfwTerminate();
|
||||
glfwSetErrorCallback(null).free();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Setup an error callback. The default implementation
|
||||
// will print the error message in System.err.
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
|
||||
// Initialize GLFW. Most GLFW functions will not work before doing this.
|
||||
if ( !glfwInit() )
|
||||
throw new IllegalStateException("Unable to initialize GLFW");
|
||||
|
||||
// Configure GLFW
|
||||
glfwDefaultWindowHints(); // optional, the current window hints are already the default
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
|
||||
|
||||
// Create the window
|
||||
window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL);
|
||||
if ( window == NULL )
|
||||
throw new RuntimeException("Failed to create the GLFW window");
|
||||
|
||||
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
|
||||
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
|
||||
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
|
||||
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
|
||||
});
|
||||
|
||||
// Get the thread stack and push a new frame
|
||||
try ( MemoryStack stack = stackPush() ) {
|
||||
IntBuffer pWidth = stack.mallocInt(1); // int*
|
||||
IntBuffer pHeight = stack.mallocInt(1); // int*
|
||||
|
||||
// Get the window size passed to glfwCreateWindow
|
||||
glfwGetWindowSize(window, pWidth, pHeight);
|
||||
|
||||
// Get the resolution of the primary monitor
|
||||
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||
|
||||
// Center the window
|
||||
glfwSetWindowPos(
|
||||
window,
|
||||
(vidmode.width() - pWidth.get(0)) / 2,
|
||||
(vidmode.height() - pHeight.get(0)) / 2
|
||||
);
|
||||
} // the stack frame is popped automatically
|
||||
|
||||
// Make the OpenGL context current
|
||||
glfwMakeContextCurrent(window);
|
||||
// Enable v-sync
|
||||
glfwSwapInterval(1);
|
||||
|
||||
// Make the window visible
|
||||
glfwShowWindow(window);
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
// This line is critical for LWJGL's interoperation with GLFW's
|
||||
// OpenGL context, or any context that is managed externally.
|
||||
// LWJGL detects the context that is current in the current thread,
|
||||
// creates the GLCapabilities instance and makes the OpenGL
|
||||
// bindings available for use.
|
||||
GL.createCapabilities();
|
||||
|
||||
// Set the clear color
|
||||
glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
|
||||
|
||||
// Run the rendering loop until the user has attempted to close
|
||||
// the window or has pressed the ESCAPE key.
|
||||
while ( !glfwWindowShouldClose(window) ) {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
|
||||
|
||||
glfwSwapBuffers(window); // swap the color buffers
|
||||
|
||||
// Poll for window events. The key callback above will only be
|
||||
// invoked during this call.
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new HelloWorld().run();
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
public class Level {
|
||||
|
||||
private VertexArray background;
|
||||
private Texture bgTexture;
|
||||
|
||||
public Level(){
|
||||
float[] vertices = new float[] {
|
||||
-10.0f, -10.0f * 9.0f / 16.0f, 0.0f,
|
||||
-10.0f, 10.0f * 9.0f / 16.0f, 0.0f,
|
||||
0f, 10.0f * 9.0f / 16.0f, 0.0f,
|
||||
0f, -10.0f * 9.0f / 16.0f, 0.0f,
|
||||
};
|
||||
|
||||
byte[] indices = new byte[] {
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
|
||||
float[] tcs = new float[] {
|
||||
0, 1,
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1
|
||||
};
|
||||
|
||||
background = new VertexArray(vertices, indices, tcs);
|
||||
bgTexture = new Texture("res/bg.jpeg");
|
||||
}
|
||||
|
||||
public void render() {
|
||||
bgTexture.bind();
|
||||
Shader.BG.enable();
|
||||
background.render();
|
||||
Shader.BG.disable();
|
||||
bgTexture.unbind();
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
public class Texture {
|
||||
|
||||
private int width, height;
|
||||
private int texture;
|
||||
|
||||
public Texture(String path) {
|
||||
texture = load(path);
|
||||
}
|
||||
|
||||
private int load(String path) {
|
||||
int[] pixels = null;
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new FileInputStream(path));
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
pixels = new int[width * height];
|
||||
image.getRGB(0, 0, width, height, pixels, 0, width);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
int[] data = new int[width * height];
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
int a = (pixels[i] & 0xff000000) >> 24;
|
||||
int r = (pixels[i] & 0xff0000) >> 16;
|
||||
int g = (pixels[i] & 0xff00) >> 8;
|
||||
int b = (pixels[i] & 0xff);
|
||||
|
||||
data[i] = a << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
|
||||
int result = glGenTextures();
|
||||
glBindTexture(GL_TEXTURE_2D, result);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data));
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
|
||||
public class VertexArray {
|
||||
|
||||
private int vao, vbo, ibo, tbo;
|
||||
private int count;
|
||||
|
||||
/*
|
||||
Donne la description de l'agencement des differents buffer
|
||||
*/
|
||||
public VertexArray(float[] vertices, byte[] indices, float[] textureCoordinates){
|
||||
count = indices.length;
|
||||
|
||||
/*
|
||||
VERTEX ARRAY OBJECT
|
||||
*/
|
||||
vao = glGenVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
|
||||
/*
|
||||
VERTEX BUFFER OBJECT
|
||||
*/
|
||||
vbo = glGenBuffers();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(Shader.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0);
|
||||
glEnableVertexAttribArray(Shader.VERTEX_ATTRIB);
|
||||
|
||||
/*
|
||||
TEXTURE BUFFER OBJECT
|
||||
*/
|
||||
tbo = glGenBuffers();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(Shader.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0);
|
||||
glEnableVertexAttribArray(Shader.TCOORD_ATTRIB);
|
||||
|
||||
/*
|
||||
INDICES BUFFER OBJECT OR ELEMENT BUFFER OBJECT
|
||||
*/
|
||||
ibo = glGenBuffers();
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public void bind(){
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
}
|
||||
|
||||
public void unbind(){
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0);
|
||||
}
|
||||
|
||||
public void render(){
|
||||
bind();
|
||||
draw();
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
package engine;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
@ -27,6 +29,4 @@ public class BufferUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -6,9 +6,99 @@
|
||||
* @author François Autin
|
||||
*
|
||||
*/
|
||||
|
||||
package engine;
|
||||
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL13.*;
|
||||
import static org.lwjgl.system.MemoryUtil.*;
|
||||
|
||||
public class Engine {
|
||||
|
||||
private long window;
|
||||
|
||||
private Scene scene;
|
||||
|
||||
private boolean running;
|
||||
|
||||
public Engine(){
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if(!glfwInit()){
|
||||
// TODO Erreur d'initialisation
|
||||
}
|
||||
this.running = true;
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //On utilise la version 3.3 d'openGL
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Compatible MAC
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Le core profile est l'interface 'avancé' d'openGL
|
||||
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
this.window = glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL);
|
||||
assert this.window != NULL;
|
||||
|
||||
// On récupère les informations du moniteur principale
|
||||
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||
assert vidmode != null;
|
||||
|
||||
glfwSetWindowPos(this.window, (vidmode.width() - width)/2, (vidmode.height() - height)/2);
|
||||
|
||||
glfwSetKeyCallback(window, new Input());
|
||||
|
||||
// Contexte = zone cible des rendus
|
||||
glfwMakeContextCurrent(this.window);
|
||||
glfwShowWindow(this.window);
|
||||
GL.createCapabilities();
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
System.out.println("OpenGL: " + glGetString(GL_VERSION));
|
||||
|
||||
|
||||
this.scene = new Scene();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
TODO gérer les input dans une autre classe
|
||||
*/
|
||||
private void processInput(){
|
||||
if(glfwGetKey(this.window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(this.window, true);
|
||||
}
|
||||
|
||||
private void update(){
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
private void render(){
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT); //SWAP avec le precedent
|
||||
scene.render();
|
||||
int error = glGetError();
|
||||
if (error != GL_NO_ERROR) System.out.println(error);
|
||||
glfwSwapBuffers(window); //Envoie le buffer vers le moniteur
|
||||
}
|
||||
|
||||
public void run(){
|
||||
init();
|
||||
while(running){
|
||||
processInput();
|
||||
update();
|
||||
render();
|
||||
if(glfwWindowShouldClose(window)) running = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Engine engine = new Engine();
|
||||
engine.run();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
package engine;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
@ -1,3 +1,5 @@
|
||||
package engine;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWKeyCallback;
|
||||
|
||||
@ -8,6 +10,5 @@ public class Input extends GLFWKeyCallback {
|
||||
@Override
|
||||
public void invoke(long window, int key, int scancode, int action, int mods) {
|
||||
keys[key] = action != GLFW.GLFW_RELEASE;
|
||||
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
package engine;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public class Matrix4f {
|
||||
@ -14,8 +16,8 @@ public class Matrix4f {
|
||||
for (int i = 0; i < SIZE; i++){
|
||||
result.elements[i] = 0.0f;
|
||||
}
|
||||
result.elements[0] = 1.0f;
|
||||
result.elements[1 + 4] = 1.0f;
|
||||
result.elements[0 + 0 * 4] = 1.0f;
|
||||
result.elements[1 + 1 * 4] = 1.0f;
|
||||
result.elements[2 + 2 * 4] = 1.0f;
|
||||
result.elements[3 + 3 * 4] = 1.0f;
|
||||
return result;
|
25
src/engine/Scene.java
Normal file
25
src/engine/Scene.java
Normal file
@ -0,0 +1,25 @@
|
||||
package engine;
|
||||
|
||||
public class Scene {
|
||||
|
||||
float[] vertices;
|
||||
|
||||
VertexArray vertexArray;
|
||||
Shader shader;
|
||||
|
||||
public Scene(){
|
||||
this.vertices = new float[] {
|
||||
-0.5f, -0.5f, 0.0f, //left
|
||||
0.5f, -0.5f, 0.0f, //right
|
||||
0.0f, 0.5f, 0.0f //top
|
||||
};
|
||||
this.shader = new Shader("shaders/vert.vert", "shaders/frag.frag");
|
||||
this.vertexArray = new VertexArray(this.vertices);
|
||||
}
|
||||
|
||||
public void render(){
|
||||
this.shader.enable();
|
||||
this.vertexArray.render();
|
||||
this.shader.disable();
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
package engine;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -5,11 +7,6 @@ import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
public class Shader {
|
||||
|
||||
public static final int VERTEX_ATTRIB = 0;
|
||||
public static final int TCOORD_ATTRIB = 1;
|
||||
|
||||
public static Shader BG;
|
||||
|
||||
private boolean enabled = false;
|
||||
|
||||
//Identifiant du programme resultat de la compilation des shaders
|
||||
@ -23,10 +20,6 @@ public class Shader {
|
||||
ID = ShaderUtils.load(vertex, fragment);
|
||||
}
|
||||
|
||||
public static void loadAll(){
|
||||
BG = new Shader("shaders/bg.vert", "shaders/bg.frag");
|
||||
}
|
||||
|
||||
public int getUniform(String name){
|
||||
if (locationCache.containsKey(name)) return locationCache.get(name);
|
||||
int result = glGetUniformLocation(ID, name);
|
@ -1,3 +1,4 @@
|
||||
package engine;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
@ -1,3 +1,5 @@
|
||||
package engine;
|
||||
|
||||
public class Vector3f {
|
||||
|
||||
public float x, y, z;
|
47
src/engine/VertexArray.java
Normal file
47
src/engine/VertexArray.java
Normal file
@ -0,0 +1,47 @@
|
||||
package engine;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
public class VertexArray {
|
||||
|
||||
private int VAO ,VBO;
|
||||
|
||||
public VertexArray(float[] vertices){
|
||||
// VERTEX ARRAY OBJECT
|
||||
VAO = glGenVertexArrays();
|
||||
|
||||
// VERTEX BUFFER OBJECT
|
||||
VBO = glGenBuffers();
|
||||
glBindVertexArray(VAO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
}
|
||||
|
||||
public void bind(){
|
||||
glBindVertexArray(this.VAO);
|
||||
}
|
||||
|
||||
public void unbind(){
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
public void render(){
|
||||
bind();
|
||||
draw();
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user