Using VAOs and VBOs I've come up with this code that renders a bunch of cubes into one huge block, all with the same set color scheme:
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#define WIDTH 0x320
#define HEIGHT 0x258
#define GLUT_KEY_LSHIFT 112
#define QUIT 0x11
#define ESC 0x1B
typedef char* string;
typedef struct vertex {
float x;
float y;
float z;
} Vertex;
float angle = 0.0;
float deltaX, deltaY;
float deltaS, deltaSt;
float deltaUp;
float vx, vz, vy;
float x = 0.0f, y = 0.0f, z = 0.0f;
float defaultSpeed = 1.0f;
float speed = 1.0f;
float lastMousePosY = HEIGHT / 2;
float lastMousePosX = WIDTH / 2;
int nFrames;
double lastTime;
GLint up, down;
GLuint vbo0, vbo1, ibo, vao;
GLint indexAmnt, vertexAmnt, colorAmnt;
GLint menuId;
GLenum mode;
int release;
int xBlocks = 30;
int yBlocks = 4;
int zBlocks = 16;
void direction(float delta_h, float delta_v)
{
angle += delta_h;
vx = sin(angle);
vz = -cos(angle);
vy += sin(delta_v) * cos(delta_v);
}
void position(float delta_x, float delta_z)
{
x += delta_z * vx * speed;
z += delta_z * vz * speed;
x += delta_x * -vz * speed;
z += delta_x * vx * speed;
}
int toRGBA(int r, int g, int b, int a)
{
return ((a << 24) | (b << 16) | (g << 8) | r);
}
Vertex initVector(float x, float y, float z)
{
Vertex v = {.x=x, .y=y, .z=z};
return v;
}
unsigned int initVertexBuffer(Vertex* v)
{
unsigned int vbo_v;
glGenBuffers(1, &vbo_v);
glBindBuffer(GL_ARRAY_BUFFER, vbo_v);
glBufferData(GL_ARRAY_BUFFER, vertexAmnt * sizeof(Vertex), v, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return vbo_v;
}
unsigned int initColorBuffer(int* c)
{
unsigned int vbo_c;
glGenBuffers(1, &vbo_c);
glBindBuffer(GL_ARRAY_BUFFER, vbo_c);
glBufferData(GL_ARRAY_BUFFER, colorAmnt * sizeof(int), c, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return vbo_c;
}
unsigned int initIndexBuffer(int *i)
{
unsigned int ibo_i;
glGenBuffers(1, &ibo_i);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_i);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexAmnt * sizeof(int), i, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return ibo_i;
}
void menuActions(int value)
{
switch(value)
{
case 1:
mode = GL_LINES;
break;
case 0:
mode = GL_TRIANGLES;
break;
default:
break;
}
}
int createMenu()
{
int menu;
menu = glutCreateMenu(menuActions);
glutAddMenuEntry("On", 1);
glutAddMenuEntry("Off", 0);
return menu;
}
unsigned int initVertexArray()
{
GLuint vao_a;
glGenVertexArrays(1, &vao_a);
glBindVertexArray(vao_a);
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(int), 0);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vbo0);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBindVertexArray(0);
return vao_a;
}
void loadgame()
{
Vertex vertices[] = {
initVector(-0.5f, -0.5f, 0.5f),
initVector(0.5f, -0.5f, 0.5f),
initVector(0.5f, 0.5f, 0.5f),
initVector(-0.5f, 0.5f, 0.5f),
initVector(-0.5f, -0.5f, -0.5f),
initVector(0.5f, -0.5f, -0.5f),
initVector(0.5f, 0.5f, -0.5f),
initVector(-0.5f, 0.5f, -0.5f)
};
int colors[] = {
toRGBA(0xff, 0x00, 0x00, 0xff),
toRGBA(0xff, 0xa5, 0x00, 0xff),
toRGBA(0xa5, 0x2a, 0x2a, 0xff),
toRGBA(0x80, 0x00, 0x00, 0xff),
toRGBA(0x00, 0x00, 0xff, 0xff),
toRGBA(0x00, 0xff, 0xff, 0xff),
toRGBA(0x00, 0x80, 0x00, 0xff),
toRGBA(0xad, 0xff, 0x2f, 0xff)
};
int indices[] = {
0, 1, 2, 2, 3, 0,
3, 2, 6, 6, 7, 3,
7, 6, 5, 5, 4, 7,
4, 0, 3, 3, 7, 4,
0, 1, 5, 5, 4, 0,
1, 5, 6, 6, 2, 1
};
vertexAmnt = sizeof(vertices) / sizeof(Vertex);
colorAmnt = sizeof(colors) / sizeof(int);
indexAmnt = sizeof(indices) / sizeof(int);
vbo0 = initVertexBuffer(vertices);
vbo1 = initColorBuffer(colors);
ibo = initIndexBuffer(indices);
vao = initVertexArray();
mode = GL_TRIANGLES;
menuId = createMenu();
glutAttachMenu(GLUT_RIGHT_BUTTON);
glClearColor(0.6f, 0.6f, 1.0f, 1.0f);
lastTime = time(NULL);
}
void input(int key, int ix, int iy)
{
if(key == GLUT_KEY_LSHIFT){ deltaUp = -0.3f; down = 1; }
}
void input_r(int key, int ix, int iy)
{
if(key == GLUT_KEY_LSHIFT) down = 0;
}
void keyboard(unsigned char key, int kx, int ky)
{
if(key == QUIT) exit(0);
if(key == ESC) release = 1;
if(key == 'w') deltaS = 0.5f;
if(key == 's') deltaS = -0.5f;
if(key == 'a') deltaSt = -0.5f;
if(key == 'd') deltaSt = 0.5f;
if(key == 'r') speed = defaultSpeed * 5.0;
if(key == ' '){ deltaUp = 0.3f; up = 1; }
}
void keyboard_r(unsigned char key, int kx, int ky)
{
if(key == ESC) release = 0;
if(key == 'w' || key == 's') deltaS = 0.0f;
if(key == 'a' || key == 'd') deltaSt = 0.0f;
if(key == ' ') up = 0;
if(key == 'r') speed = defaultSpeed;
}
void resetPointer()
{
glutWarpPointer(WIDTH / 2.0, HEIGHT / 2.0);
lastMousePosY = HEIGHT / 2.0;
lastMousePosX = WIDTH / 2.0;
}
void mouse(int mx, int my)
{
if(!release)
{
if(fabs(WIDTH / 2 - mx) > 15 ||
fabs(HEIGHT / 2 - my) > 15)
resetPointer();
else
{
int vMotion = lastMousePosY - my;
int hMotion = lastMousePosX - mx;
deltaX = -hMotion / 25.0;
deltaY = vMotion / 25.0;
lastMousePosY = my;
lastMousePosX = mx;
}
}
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const double w = glutGet(GLUT_WINDOW_WIDTH);
const double h = glutGet(GLUT_WINDOW_HEIGHT);
gluPerspective(45.0, w / h, 0.1, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(x, y, z, x + vx, y + vy, z + vz, 0.0f, 1.0f, 0.0f);
glRotatef(angle, 0.0, 1.0, 0.0);
int i = 0, j = 0, k = 0;
for(; i < yBlocks; i ++)
for(j = -xBlocks / 2; j < xBlocks / 2; j ++)
for(k = -zBlocks / 2; k < zBlocks / 2; k ++)
{
glPushMatrix();
glTranslatef((float)j, -(float)i - 1.7f, (float)k);
glBindVertexArray(vao);
glDrawElements(mode, indexAmnt, GL_UNSIGNED_INT, NULL);
glBindVertexArray(0);
glPopMatrix();
}
glutSwapBuffers();
}
void update()
{
if(deltaX || deltaY)
direction(deltaX, deltaY);
if(deltaS || deltaSt)
position(deltaSt, deltaS);
deltaX *= 0.9f;
deltaY *= 0.9f;
if(up == 0 && down == 0)
deltaUp *= 0.9;
if(fabs(deltaUp) >= 0.001f)
y += deltaUp;
double currentTime = time(NULL);
nFrames ++;
if(currentTime - lastTime >= 1.0)
{
//printf("%f ms/frame\n", 1000.0 / (double)nFrames);
printf("%d fps\n", nFrames);
defaultSpeed = (float)nFrames / 60.0f;
nFrames = 0;
lastTime += 1.0;
}
glutPostRedisplay();
}
int main(int argc, string argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("First Person World");
glutDisplayFunc(render);
glutIdleFunc(update);
glutPassiveMotionFunc(mouse);
glutKeyboardFunc(keyboard);
glutKeyboardUpFunc(keyboard_r);
glutSpecialFunc(input);
glutSpecialUpFunc(input_r);
glutSetCursor(GLUT_CURSOR_NONE);
GLenum error = glewInit();
if(error != GLEW_OK)
{
fprintf(stderr, "Error: %s\n", glewGetErrorString(error));
return -1;
}
glewGetString(GLEW_VERSION);
loadgame();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glutMainLoop();
return 0;
}
This code works and it works well, but right away I notice it was lacking the ability to load textures. So I researched how to load and render textures, but sadly I have been unable to get it to work. What I have so far regarding loading and rendering textures is this:
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <SOIL/SOIL.h>
#define WIDTH 0x320
#define HEIGHT 0x258
#define GLUT_KEY_LSHIFT 112
#define QUIT 0x11
#define ESC 0x1B
typedef char* string;
typedef struct vertex {
float x;
float y;
float z;
float u;
float v;
} Vertex;
float angle = 0.0;
float deltaX, deltaY;
float deltaS, deltaSt;
float deltaUp;
float vx, vz, vy;
float x = 0.0f, y = 0.0f, z = 0.0f;
float defaultSpeed = 1.0f;
float speed = 1.0f;
float lastMousePosY = HEIGHT / 2;
float lastMousePosX = WIDTH / 2;
int nFrames;
double lastTime;
GLint up, down;
GLuint vbo0, vbo1, ibo, vao, texId, progId;
GLint indexAmnt, vertexAmnt, colorAmnt;
GLint menuId;
GLenum mode;
int release;
int xBlocks = 30;
int yBlocks = 4;
int zBlocks = 16;
void direction(float delta_h, float delta_v)
{
angle += delta_h;
vx = sin(angle);
vz = -cos(angle);
vy += sin(delta_v) * cos(delta_v);
}
void position(float delta_x, float delta_z)
{
x += delta_z * vx * speed;
z += delta_z * vz * speed;
x += delta_x * -vz * speed;
z += delta_x * vx * speed;
}
Vertex initVertex(float x, float y, float z, float u, float v)
{
Vertex v0 = {.x=x, .y=y, .z=z, .u=u, .v=v};
return v0;
}
unsigned int initShaderProgram()
{
unsigned int id_p, vs, fs;
const char* vertShader =
"#version 300 es\n"
"in vec2 texcoord;"
"out vec2 TexCoord;"
"void main() {"
" TexCoord = texcoord;"
"}";
const char* fragShader =
"#version 300 es\n"
"uniform sampler2D cubeTex;"
"in vec2 TexCoord;"
"void main() {"
" gl_FragColor = texture(cubeTex, TexCoord);"
"}";
int compile_ok = 0;
vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertShader, NULL);
glCompileShader(vs);
glGetShaderiv(vs, GL_COMPILE_STATUS, &compile_ok);
if(compile_ok == 0)
{
GLsizei slen = 0;
GLint blen = 0;
glGetShaderiv(vs, GL_INFO_LOG_LENGTH , &blen);
fprintf(stderr, "Error in vertex shader:\n");
GLchar* compiler_log = (GLchar*)malloc(blen);
glGetShaderInfoLog(vs, blen, &slen, compiler_log);
fprintf(stderr, "%s\n", compiler_log);
free (compiler_log);
}
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragShader, NULL);
glCompileShader(fs);
glGetShaderiv(vs, GL_COMPILE_STATUS, &compile_ok);
if(compile_ok == 0)
{
GLsizei slen = 0;
GLint blen = 0;
glGetShaderiv(fs, GL_INFO_LOG_LENGTH , &blen);
fprintf(stderr, "Error in fragment shader:\n");
GLchar* compiler_log = (GLchar*)malloc(blen);
glGetShaderInfoLog(fs, blen, &slen, compiler_log);
fprintf(stderr, "%s\n", compiler_log);
free (compiler_log);
}
id_p = glCreateProgram();
glAttachShader(id_p, vs);
glAttachShader(id_p, fs);
glLinkProgram(id_p);
return id_p;
}
unsigned int initVertexBuffer(Vertex* v)
{
unsigned int vbo_v;
glGenBuffers(1, &vbo_v);
glBindBuffer(GL_ARRAY_BUFFER, vbo_v);
glBufferData(GL_ARRAY_BUFFER, vertexAmnt * sizeof(Vertex), v, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return vbo_v;
}
unsigned int initColorBuffer(int* c)
{
unsigned int vbo_c;
glGenBuffers(1, &vbo_c);
glBindBuffer(GL_ARRAY_BUFFER, vbo_c);
glBufferData(GL_ARRAY_BUFFER, colorAmnt * sizeof(int), c, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return vbo_c;
}
unsigned int initIndexBuffer(int *i)
{
unsigned int ibo_i;
glGenBuffers(1, &ibo_i);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_i);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexAmnt * sizeof(int), i, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return ibo_i;
}
unsigned int initTexture(const char* filename)
{
unsigned int id_t;
glGenTextures(1, &id_t);
int w, h;
unsigned char* image = SOIL_load_image(filename, &w, &h, 0, SOIL_LOAD_RGB);
glBindTexture(GL_TEXTURE_2D, id_t);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
return id_t;
}
void menuActions(int value)
{
switch(value)
{
case 1:
mode = GL_LINES;
break;
case 0:
mode = GL_TRIANGLES;
break;
default:
break;
}
}
int createMenu()
{
int menu;
menu = glutCreateMenu(menuActions);
glutAddMenuEntry("On", 1);
glutAddMenuEntry("Off", 0);
return menu;
}
unsigned int initVertexArray()
{
GLuint vao_a;
glGenVertexArrays(1, &vao_a);
glBindVertexArray(vao_a);
glBindBuffer(GL_ARRAY_BUFFER, vbo0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)offsetof(Vertex, x));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)offsetof(Vertex, u));
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (GLvoid*)offsetof(Vertex, x));
glEnableClientState(GL_VERTEX_ARRAY);
glActiveTexture(GL_TEXTURE0);
glUniform1i(glGetUniformLocation(progId, "cubeTex"), 0);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid*)offsetof(Vertex, u));
glBindTexture(GL_TEXTURE_2D, texId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBindVertexArray(0);
return vao_a;
}
void loadgame()
{
Vertex vertices[] = {
initVertex(-0.5f, -0.5f, 0.5f, 0.0f, 0.0f),
initVertex(0.5f, -0.5f, 0.5f, 1.0f, 0.0f),
initVertex(0.5f, 0.5f, 0.5f, 1.0f, 1.0f),
initVertex(-0.5f, 0.5f, 0.5f, 0.0f, 1.0f),
initVertex(-0.5f, -0.5f, -0.5f, 0.0f, 0.0f),
initVertex(0.5f, -0.5f, -0.5f, 1.0f, 0.0f),
initVertex(0.5f, 0.5f, -0.5f, 1.0f, 1.0f),
initVertex(-0.5f, 0.5f, -0.5f, 0.0f, 1.0f)
};
int indices[] = {
0, 1, 2, 2, 3, 0,
3, 2, 6, 6, 7, 3,
7, 6, 5, 5, 4, 7,
4, 0, 3, 3, 7, 4,
0, 1, 5, 5, 4, 0,
1, 5, 6, 6, 2, 1
};
vertexAmnt = sizeof(vertices) / sizeof(Vertex);
indexAmnt = sizeof(indices) / sizeof(int);
vbo0 = initVertexBuffer(vertices);
ibo = initIndexBuffer(indices);
texId = initTexture("stone.png");
progId = initShaderProgram();
vao = initVertexArray();
mode = GL_TRIANGLES;
menuId = createMenu();
glutAttachMenu(GLUT_RIGHT_BUTTON);
glClearColor(0.6f, 0.6f, 1.0f, 1.0f);
lastTime = time(NULL);
}
void input(int key, int ix, int iy)
{
if(key == GLUT_KEY_LSHIFT){ deltaUp = -0.3f; down = 1; }
}
void input_r(int key, int ix, int iy)
{
if(key == GLUT_KEY_LSHIFT) down = 0;
}
void keyboard(unsigned char key, int kx, int ky)
{
if(key == QUIT) exit(0);
if(key == ESC) release = 1;
if(key == 'w') deltaS = 0.5f;
if(key == 's') deltaS = -0.5f;
if(key == 'a') deltaSt = -0.5f;
if(key == 'd') deltaSt = 0.5f;
if(key == 'r') speed = defaultSpeed * 5.0;
if(key == ' '){ deltaUp = 0.3f; up = 1; }
}
void keyboard_r(unsigned char key, int kx, int ky)
{
if(key == ESC) release = 0;
if(key == 'w' || key == 's') deltaS = 0.0f;
if(key == 'a' || key == 'd') deltaSt = 0.0f;
if(key == ' ') up = 0;
if(key == 'r') speed = defaultSpeed;
}
void resetPointer()
{
glutWarpPointer(WIDTH / 2.0, HEIGHT / 2.0);
lastMousePosY = HEIGHT / 2.0;
lastMousePosX = WIDTH / 2.0;
}
void mouse(int mx, int my)
{
if(!release)
{
if(fabs(WIDTH / 2 - mx) > 15 ||
fabs(HEIGHT / 2 - my) > 15)
resetPointer();
else
{
int vMotion = lastMousePosY - my;
int hMotion = lastMousePosX - mx;
deltaX = -hMotion / 25.0;
deltaY = vMotion / 25.0;
lastMousePosY = my;
lastMousePosX = mx;
}
}
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const double w = glutGet(GLUT_WINDOW_WIDTH);
const double h = glutGet(GLUT_WINDOW_HEIGHT);
gluPerspective(45.0, w / h, 0.1, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(x, y, z, x + vx, y + vy, z + vz, 0.0f, 1.0f, 0.0f);
glRotatef(angle, 0.0, 1.0, 0.0);
glUseProgram(progId);
int i = 0, j = 0, k = 0;
for(; i < yBlocks; i ++)
for(j = -xBlocks / 2; j < xBlocks / 2; j ++)
for(k = -zBlocks / 2; k < zBlocks / 2; k ++)
{
glPushMatrix();
glTranslatef((float)j, -(float)i - 1.7f, (float)k);
glBindVertexArray(vao);
//glActiveTexture(GL_TEXTURE0);
//glUniform1i(glGetUniformLocation(progId, "cubeTex"), 0);
//glBindTexture(GL_TEXTURE_2D, texId);
glDrawElements(mode, indexAmnt, GL_UNSIGNED_INT, NULL);
glBindVertexArray(0);
glPopMatrix();
}
glutSwapBuffers();
}
void update()
{
if(deltaX || deltaY)
direction(deltaX, deltaY);
if(deltaS || deltaSt)
position(deltaSt, deltaS);
deltaX *= 0.9f;
deltaY *= 0.9f;
if(up == 0 && down == 0)
deltaUp *= 0.9;
if(fabs(deltaUp) >= 0.001f)
y += deltaUp;
double currentTime = time(NULL);
nFrames ++;
if(currentTime - lastTime >= 1.0)
{
printf("%d fps\n", nFrames);
defaultSpeed = (float)nFrames / 60.0f;
nFrames = 0;
lastTime += 1.0;
}
glutPostRedisplay();
}
int main(int argc, string argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("First Person World");
glutDisplayFunc(render);
glutIdleFunc(update);
glutPassiveMotionFunc(mouse);
glutKeyboardFunc(keyboard);
glutKeyboardUpFunc(keyboard_r);
glutSpecialFunc(input);
glutSpecialUpFunc(input_r);
glutSetCursor(GLUT_CURSOR_NONE);
GLenum error = glewInit();
if(error != GLEW_OK)
{
fprintf(stderr, "Error: %s\n", glewGetErrorString(error));
return -1;
}
glewGetString(GLEW_VERSION);
loadgame();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glutMainLoop();
return 0;
}
I realize that there may very well be many other things I need to fix, even within my working code, which is to be expected as I am still learning. So, I'd like to know if there's a way I can still get this code to work with textures (maybe I'm just missing something small?) or if I've been going about implementing textures in this code entirely (in which case, how would I go about it?).