Related
i'm trying to learn something about computer graphics, and i'm using OpenGL with C (i know, i like pain haha).
Right now i'm following the tutorials from learnopengl.com, but althought everything seems fine to me, nothing get draw to the screen... whene i "turn" wireframe mode on, just a single line gets drawn.
Here's the code so far (i'm sorry if it's too long or messy):
main.cpp
#define m_DEBUG_MODE
#include <Engine/Engine.h>
int main()
{
/*===============================================*/
GLFWwindow* window;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
if (!glfwInit()) {
printf("Could not initialize GLFW...");
return -1;
}
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window) {
printf("Could not create the window...");
glfwTerminate();
return -1;
}
glfwSwapInterval(1);
glfwMakeContextCurrent(window);
GLenum err = glewInit();
if (GLEW_OK != err) {
printf("Could not initialize GLEW...");
return -1;
}
printf("%s\n", glGetString(GL_VERSION));
list_t scene;
list_new(scene, mesh_t);
/*===============================================*/
float vertices[] = {
/* posistion | rgba */
-0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.5f, 1.0f, // 0
0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 1.0f, 0.0f, 1.0f, // 1
0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.0f, 1.0f, 1.0f, // 2
-0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 3
};
float indices[6] = {
0, 1, 2, // tri. 1
2, 3, 0, // tri. 2
};
buffer_data_t vb = { .size=sizeof(vertices), .data=vertices, .usage=GL_STATIC_DRAW };
buffer_data_t eb = { .size=sizeof(indices), .data=indices, .usage=GL_STATIC_DRAW };
attrib_layout_t ap[2] = {
{ .index=0, .size=4, .type=GL_FLOAT, .normalized=GL_FALSE, .stride=sizeof(float) * 8, .pointer=(void*)(0 * sizeof(float)) }, // position
{ .index=1, .size=4, .type=GL_FLOAT, .normalized=GL_FALSE, .stride=sizeof(float) * 8, .pointer=(void*)(4 * sizeof(float)) }, // color
};
objectID m_shader = create_shader("./res/shaders/vertex.shader", "./res/shaders/fragment.shader");
mesh_t mesh1;
DEBUG_GL(mesh_init(
&mesh1, 4,
vb, ap, sizeof(ap) / sizeof(attrib_layout_t), eb,
m_shader
));
list_add(scene, mesh_t, mesh1);
unbind_all();
/*===============================================*/
while (!glfwWindowShouldClose(window))
{
// RENDERING
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
for (unsigned int i=0; i<scene.size; i++) {
DEBUG_GL(render(list_get(scene, mesh_t, i), GL_TRIANGLES, 0));
}
glfwSwapBuffers(window);
glfwPollEvents();
}
/*===============================================*/
list_delete(scene, mesh_t);
return 0;
}
Engine.h
#ifndef M_TOOLS
#define M_TOOLS
#define GLEW_STATIC
#include <GL/glew.h>
#define GLFW_DLL
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// remove "unused" warnings
#define USE( x ) (void)x
/*============
List
============*/
typedef struct list_t {
void * data; // typeless data pointer
unsigned int size; // count of elements
}list_t;
#define list_new( _list, T )\
{\
T* temp_data = (T*)malloc( sizeof(T) * 0 );\
_list.data = (void*)temp_data;\
_list.size = 0;\
}
#define list_add( _list, T, val )\
{ /* make it a scoped block ( so it deletes temp_data ) */\
/* create a temp array to store old data */\
T* temp_data = (T*)malloc( sizeof(T) * _list.size );\
if (_list.data != NULL) {\
for (unsigned int i=0; i<_list.size; i++) {\
temp_data[i] = ((T*)_list.data)[i];\
}\
}\
/* clear the old data, create a new array with the right size,
and put the old values (+ the new one) inside of it */\
free( (T*)_list.data );\
_list.size += 1;\
_list.data = (void*)malloc( sizeof(T) * _list.size );\
for (unsigned int i=0; i<_list.size - 1; i++) {\
((T*)_list.data)[i] = temp_data[i];\
}\
((T*)_list.data)[_list.size - 1] = val;\
free( temp_data );\
}
#define list_get( _list, T, index )\
(index < _list.size) ? &((T*)_list.data)[index] : NULL
#define list_remove( _list, T, index )\
{\
T* temp_data = (T*)malloc( sizeof(T) * _list.size );\
for ( unsigned int i=0; i<_list.size; i++ ) {\
temp_data[i] = ((T*)_list.data)[i];\
}\
_list.size -= 1;\
for (unsigned int i=0; i<_list.size; i++) {\
if (i != index) {\
*((T*)_list.data + i) = temp_data[i];\
} else {\
continue;\
}\
}\
}
#define list_delete( _list, T )\
free( (T*)(_list.data) );\
_list.data = NULL;\
_list.size = 0;
/*==============
misc
==============*/
typedef unsigned int objectID;
// unbind stuff
void unbind_all() {
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glUseProgram(0);
}
void ClearGLErrors() {
while (glGetError() != GL_NO_ERROR);
}
int LogGLErrors(const char* file, const int line, const char* function) {
int stop = 0;
GLenum error = glGetError();
while (error) {
printf("[OpenGL Error: %d] %s : %d\n%s\n", error, file, line, function);
stop = 1;
error = glGetError();
}
if (stop)
return 0;
else
return 1;
}
#define ASSERT(x) if (!(x)) exit(-1)
#ifdef m_DEBUG_MODE
#define DEBUG_GL(x) ClearGLErrors();\
x;\
ASSERT(LogGLErrors(__FILE__, __LINE__, #x))
#endif
#ifndef m_DEBUG_MODE
#define DEBUG_GL(x) x;
#endif
// append a char to a string
char* append_to_str(char* str, char c) {
size_t len = strlen(str);
/* one for extra char, one for trailing zero */
char *str2 = malloc(len + 1 + 1);
strcpy(str2, str);
str2[len] = c;
str2[len + 1] = '\0';
// free temp str and return res
free(str2);
return str;
}
// return a copy of the string
char* str_copy(char* str) {
char* new_str = "";
for (char* c=str; *c!='\0'; c++) {
append_to_str(new_str, *c);
}
return new_str;
}
// read a file and dump its content in a string
char* read_file(char* filepath) {
FILE* file;
char* buffer;
long numbytes;
// open file and search for EOF
file = fopen(filepath, "r");
fseek(file, 0L, SEEK_END);
numbytes = ftell(file);
fseek(file, 0L, SEEK_SET);
buffer = (char*)calloc(numbytes, sizeof(char));
// dump data into the buffer string
fread(buffer, sizeof(char), numbytes, file);
fclose(file);
return buffer;
//free(buffer); <-- idk
}
/*===============
Shaders
===============*/
// errors logging process
#define shader_error(id, status_type, iv, info_log, delete, index)\
int log_info##index;\
iv(id, status_type, &log_info##index);\
if (log_info##index == GL_FALSE) {\
int len;\
iv(id, GL_INFO_LOG_LENGTH, &len);\
char* error_message = (char*)malloc(sizeof(char*) * len);\
info_log(id, len, &len, error_message);\
printf("%s\n", error_message);\
delete(id);\
return 0;\
}
objectID compile_shader(char* source_filepath, GLenum type) {
// parse shader source file
char* source_string = read_file(source_filepath);
// create shader object
objectID shader_id = glCreateShader(type);
glShaderSource(shader_id, 1, (const GLchar * const*)(&source_string), NULL);
glCompileShader(shader_id);
// check and log errors during compilation
shader_error(shader_id, GL_COMPILE_STATUS, glGetShaderiv, glGetShaderInfoLog, glDeleteShader, 0);
return shader_id;
}
objectID create_shader(char* vertex_filepath, char* fragment_filepath) {
// create the program, and attach compiled shaders
objectID program = glCreateProgram();
objectID vs = compile_shader(vertex_filepath, GL_VERTEX_SHADER);
objectID fs = compile_shader(fragment_filepath, GL_FRAGMENT_SHADER);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glValidateProgram(program);
// check and log errors during program creation
shader_error(program, GL_ATTACHED_SHADERS, glGetProgramiv, glGetProgramInfoLog, glDeleteProgram, 0);
shader_error(program, GL_LINK_STATUS, glGetProgramiv, glGetProgramInfoLog, glDeleteProgram, 1);
shader_error(program, GL_VALIDATE_STATUS, glGetProgramiv, glGetProgramInfoLog, glDeleteProgram, 2);
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}
/*===============
Mesh object
===============*/
typedef struct buffer_data_t {
GLsizeiptr size;
const GLvoid * data;
GLenum usage;
}buffer_data_t;
typedef struct attrib_layout_t {
GLuint index;
GLint size;
GLenum type;
GLboolean normalized;
GLsizei stride;
const GLvoid * pointer;
}attrib_layout_t;
typedef struct mesh_t { // actual mesh object
objectID vao; // vertex array object
objectID vbo; // vertex buffer object
objectID ebo; // element buffer object
objectID shader; // shader program
unsigned int vert_count; // vertices count
}mesh_t;
void mesh_bind(mesh_t* mesh) {
unbind_all();
// bind mesh components
DEBUG_GL(glBindVertexArray(mesh->vao));
DEBUG_GL(glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo));
DEBUG_GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ebo));
DEBUG_GL(glUseProgram(mesh->shader));
}
void mesh_init(
mesh_t* mesh, unsigned int _vert_count, // mesh object
buffer_data_t vb, // vertex buffer
attrib_layout_t* ap, unsigned int ap_n, // attribute pointers
buffer_data_t eb, // element buffer
objectID shader_program // shader
) {
unbind_all();
mesh->vert_count = _vert_count;
mesh->shader = shader_program;
// vertex array
DEBUG_GL(glGenVertexArrays(1, &mesh->vao));
DEBUG_GL(glBindVertexArray(mesh->vao));
// vertex buffer object
DEBUG_GL(glGenBuffers(1, &mesh->vbo));
DEBUG_GL(glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo));
DEBUG_GL(glBufferData(GL_ARRAY_BUFFER, vb.size, vb.data, vb.usage));
// element buffer object
DEBUG_GL(glGenBuffers(1, &mesh->ebo));
DEBUG_GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ebo));
DEBUG_GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, eb.size, eb.data, eb.usage));
// attribute pointers
for (unsigned int i=0; i<ap_n; i++) {
DEBUG_GL(glVertexAttribPointer(ap[i].index, ap[i].size, ap[i].type, ap[i].normalized, ap[i].stride, ap[i].pointer));
DEBUG_GL(glEnableVertexAttribArray(ap[i].index));
}
unbind_all();
}
/*===============
Renderer
===============*/
void render(mesh_t* mesh, GLenum draw_mode, int wireframe) {
// wireframe mode (polygon fill - polygon line)
if (wireframe) { glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); }
else { glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); }
// bind - draw - unbind
mesh_bind(mesh);
glDrawElements( draw_mode, mesh->vert_count, GL_UNSIGNED_INT, NULL );
unbind_all();
}
#endif /* M_TOOLS */
Tell me if i'm doing anything wrong, i just can't find anything really (although, i think it has something to do with the shaders)...
The type of the indices needs to be integral and correspond to the type specified in the draw call:
float indices[6] = {
unsigned int indices[6] = {
0, 1, 2, // tri. 1
2, 3, 0, // tri. 2
};
The type specifier GL_UNSIGNED_INT is used in the glDrawElements instruction. Therefore the type of the indices must be unsigned int.
Recently I've been trying to learn SDL, a graphics library amongst other things, for C. I haven't gotten very far but with the basics I've learnt from this tutorial (Yes, I know it's for C++ but it seems the majority of things are still the same), I've tried to create a "template" SDL program to start all my other future programs from. This is what I had:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface);
void closeProg(SDL_Window *window);
SDL_Surface *loadImage(char *path);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface);
closeProg(window);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window) {
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
The program is divided into three sections: one to initialize SDL, one where the main program occurs, and one to close SDL and free the surfaces.
I realized the problem with this is that if I had created another surface then I would have to add code to the closeProg() function to free the surface at the end. N.B: Keep in mind that these "surfaces" are just pointers to the actual thing, which as far as I'm aware you don't really interact with, you just deal with the pointer to it.
To work around this, I decided to create an array of these pointers-to-surfaces, and then make a function that creates a surface and adds it's pointer to the array. This would allow me at the end in the closeProg() function to go through each surface and free it, then free the array too. (Note that the window's surface is not added to this list as it gets freed automatically with the SDL_DestroyWindow() function)
Here is the declaration of this new array:
// The pointer to the array. Currently NULL until something is added
SDL_Surface **surfaces = NULL;
// Keeps track of the size of the array
size_t surfaceCount = 0;
This is the function that adds to the array:
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
And here it is in use. Note that loadImage() returns a surface from an image, and showSurfaces() prints out the contents of the surfaces array.
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
Up to the first two surfaces, everything went well. However, when I tried to add the third surface, the program hanged immediately before closing (indication that something is wrong in the closeProg() function?). I decided to print out the contents of the array and here is what I got.
No current surfaces.
Current surfaces:
Index 0: 00753C98
Current surfaces:
Index 0: 00753C98
Index 1: 00754780
Current surfaces:
Index 0: 02805150
Index 1: 008F00C0
Index 2: 201339FC
In the first two print-outs, everything seemed well but in the third one, you can notice that the previous addresses have changed. This happened repeatedly and no matter how many surfaces I added. After the first two reallocations, the array's content kept on changing.
I think that is why the program hangs when closing, because in the closeProg function, the program is told to free an unknown pointer that is not a surface, hence it crashes. Not to mention that I also set that pointer to NULL, and who knows what else that could cause.
Is this changing of content of the array normal? And if not, I would really appreciate if you could help me find what is causing this strange behavior. I repeat that I am a COMPLETE BEGINNER in this and so any help, even in a matter not relating to this question would be GREATLY appreciated. Thank you in advance :)
For reference, here are the images I used.
Here is the full code if you're interested:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount);
SDL_Surface *loadImage(char *path);
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
// The pointer to the array. Currently NULL untill something is added
SDL_Surface **surfaces = (SDL_Surface *)calloc(1, sizeof(*surfaces));
// Keeps track of the size of the array
size_t surfaceCount = 0;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface, surfaces, &surfaceCount);
closeProg(window, surfaces, surfaceCount);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount) {
// Go through the array and free each surface.
for (int i = 0; i < surfaceCount; i++){
SDL_FreeSurface(*(surfaces + i));
*(surfaces + i) = NULL;
}
// Free the array itself.
free(surfaces);
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
SDL_Surface *loadImage(char *path) {
SDL_Surface *image = SDL_LoadBMP(path);
if (image == NULL) {
printf("Failed to load image.\nError: %s\n", SDL_GetError());
}
return image;
}
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
void showSurfaces(SDL_Surface **surfaces, size_t surfaceCount) {
if (surfaceCount == 0) {
printf("\nNo current surfaces.\n");
} else {
printf("\nCurrent surfaces:\n");
for (int i = 0; i < surfaceCount; i++) {
printf("\tIndex %d: %p\n", i, *(surfaces + i));
}
putchar('\n');
}
}
If you can replicate this error, please comment down below so I know it's not something wrong with my machine or anything like that.
NOTE: I am using SDL 2.0
I realized the problem with this is that if I had created another
surface then I would have to add code to the closeProg() function to
free the surface at the end.
Not really. It's cleanest to do that, but the OS can be relied upon to release the program's memory, including any remaining dynamically allocated memory, when the program terminates. You could as easily just hold the allocated memory until then, instead of freeing it explicitly just prior to termination.
Anyway,
In the first two print-outs, everything seemed well but in the third one, you can notice that the previous addresses have changed. This
happened repeatedly and no matter how many surfaces I added. After the
first two reallocations, the array's content kept on changing.
I think that is why the program hangs when closing, because in the
closeProg function, the program is told to free an unknown pointer
that is not a surface, hence it crashes. Not to mention that I also
set that pointer to NULL, and who knows what else that could cause.
I think your analysis is plausible.
Is this changing of content of the array normal?
No. Reallocation with realloc() preserves the contents of the original space, copying it to the new space if it does not overlap, up to the lesser of the sizes of the two spaces.
I would really appreciate if you could help me find what is causing
this strange behavior.
Your addSurface() function is flawed. Although you have done well with the details of the reallocation itself, accounting both for the possibility that reallocation does not occur in place and the possibility that it fails, you do not successfully convey the new pointer to the caller.
In particular, consider this excerpt:
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
The surface and surfaceCount variables are both function parameters. You seem to understand that in order to convey a new surface count value back to the caller via the latter, it must be a pointer to a variable accessible to the caller, so that the function can update that value via the pointer. That's what the last line of the excerpt does.
The situation is no different for the surfaces pointer. When in the first line of the excerpt you assign to surfaces, you are not making a change that will be visible to the caller. You're only modifying the value of a local variable inside the function. You must either add a layer of indirection for surface, or else convey the new pointer back to the caller via the function's return value. Personally, I'd go with the latter, because three-* programming is not widely accepted as good style.
I'm changing an image's pixbuf data in response to a timer event but the changes only appear if I, for example, cover the window with another window and then uncover it. How can I get the changes to appear as soon as I make them?
#include <gtk/gtk.h>
#include <stdlib.h>
#define ROWS 400
#define COLS 400 // must be divisible by 4
#define BYTES_PER_PIXEL 3
typedef struct {
GtkImage *image;
int stride;
} ImageData;
void free_pixels(guchar *pixels, gpointer data) {
free(pixels);
}
void setrgb(guchar *a, int row, int col, int stride,
guchar r, guchar g, guchar b) {
int p = row * stride + col * BYTES_PER_PIXEL;
a[p] = r; a[p+1] = g; a[p+2] = b;
}
int update_pic(gpointer data) {
static int row = 0;
if (row > 100) return FALSE;
ImageData *id = (ImageData*)data;
GdkPixbuf *pb = gtk_image_get_pixbuf(id->image);
guchar *g = gdk_pixbuf_get_pixels(pb);
for (int c = 0; c < 200; c++)
setrgb(g, row, c, id->stride, 255, 0, 0);
row++;
// this is not enough to get it to show the updated picture
gtk_widget_queue_draw(GTK_WIDGET(id->image));
// adding this does not fix it
while (g_main_context_pending(NULL))
g_main_context_iteration(NULL, FALSE);
return TRUE;
}
int main(int argc, char **argv) {
GtkWidget *window, *image;
GdkPixbuf *pb;
guchar *pixels = calloc(ROWS * COLS, BYTES_PER_PIXEL);
ImageData id;
gtk_init(&argc, &argv);
image = gtk_image_new();
id.image = GTK_IMAGE(image);
id.stride = COLS * BYTES_PER_PIXEL; // COLS is divisible by 4
pb = gdk_pixbuf_new_from_data(
pixels,
GDK_COLORSPACE_RGB, // colorspace
0, // has_alpha
8, // bits-per-sample
COLS, ROWS, // cols, rows
id.stride, // rowstride
free_pixels, // destroy_fn
NULL // destroy_fn_data
);
gtk_image_set_from_pixbuf(GTK_IMAGE(image), pb);
g_object_unref(pb); // should I do this?
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "image");
gtk_window_set_default_size(GTK_WINDOW(window), COLS, ROWS);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window), image);
g_timeout_add(250, // milliseconds
update_pic, // handler function
&id); // data
gtk_widget_show_all(window);
gtk_main();
return 0;
}
GtkImage is not a general drawing widget, for that you would need GtkDrawingArea. I think the simplest way to do what you want with the GtkImage is to update your GdkPixBuf then set the GtkImage with the updated image. I have increased the reference count to the PixBuf, but I am not 100% sure that it is needed, but it does not do any harm.
#include <gtk/gtk.h>
#include <stdlib.h>
#define ROWS 400
#define COLS 400 // must be divisible by 4
#define BYTES_PER_PIXEL 3
typedef struct {
GtkImage *image;
GdkPixbuf *pb;
int stride;
} ImageData;
void free_pixels(guchar *pixels, gpointer data) {
free(pixels);
}
void setrgb(guchar *a, int row, int col, int stride,
guchar r, guchar g, guchar b) {
int p = row * stride + col * BYTES_PER_PIXEL;
a[p] = r; a[p+1] = g; a[p+2] = b;
}
int update_pic(gpointer data) {
static int row = 0;
if (row > 100) return FALSE;
ImageData *id = (ImageData*)data;
guchar *g = gdk_pixbuf_get_pixels(id->pb);
for (int c = 0; c < 200; c++)
setrgb(g, row, c, id->stride, 255, 0, 0);
row++;
// Update the image, by setting it.
gtk_image_set_from_pixbuf(GTK_IMAGE(id->image), id->pb);
return TRUE;
}
int main(int argc, char **argv) {
GtkWidget *window;
guchar *pixels = calloc(ROWS * COLS, BYTES_PER_PIXEL);
ImageData id;
gtk_init(&argc, &argv);
id.stride = COLS * BYTES_PER_PIXEL; // COLS is divisible by 4
id.pb = gdk_pixbuf_new_from_data(
pixels,
GDK_COLORSPACE_RGB, // colorspace
0, // has_alpha
8, // bits-per-sample
COLS, ROWS, // cols, rows
id.stride, // rowstride
free_pixels, // destroy_fn
NULL // destroy_fn_data
);
id.image = GTK_IMAGE(gtk_image_new_from_pixbuf(id.pb));
g_object_ref(id.pb);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "image");
gtk_window_set_default_size(GTK_WINDOW(window), COLS, ROWS);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window), image);
g_timeout_add(250, // milliseconds
update_pic, // handler function
&id); // data
gtk_widget_show_all(window);
gtk_main();
g_object_unref(id.pb);
return 0;
}
I'm trying to load an MD2 model but I can't seem to get the vertices to draw correctly. I'm not loading UVs or normals at the moment just want to see the model appear correctly in a single frame then take it from there.
Here's my md2 structures (mostly taken from here):
struct v3
{
union
{
struct
{
union { float x; float r; };
union { float y; float g; };
union { float z; float b; };
};
float At[3];
};
};
struct md2_header
{
unsigned int Magic;
unsigned int Version;
unsigned int TextureWidth;
unsigned int TextureHeight;
unsigned int FrameSize;
unsigned int NumTextures;
unsigned int NumVertices;
unsigned int NumUVs;
unsigned int NumTrigs;
unsigned int NumGLCommands;
unsigned int NumFrames;
unsigned int OffsetTextures;
unsigned int OffsetUVs;
unsigned int OffsetTrigs;
unsigned int OffsetFrames;
unsigned int OffsetGLCommands;
unsigned int OffsetEnd;
};
struct md2_vertex
{
unsigned char At[3];
unsigned char NormalIndex;
};
struct md2_frame
{
float Scale[3];
float Translate[3];
char Name[16];
md2_vertex *Vertices;
};
struct md2_skin
{
char Name[64];
};
struct md2_uv
{
unsigned short u;
unsigend short v;
}
struct md2_triangle
{
unsigned short Vertices[3];
unsigned short UVs[3];
};
struct md2_model
{
md2_header Header;
md2_uv *UVs;
md2_triangle *Triangles;
md2_frame *Frames;
md2_skin *Skins;
int *GLCommands;
unsigned int Texture;
unsigned int VAO, VBO;
};
And here's my simple loading function:
void MD2LoadModel (char *FilePath, md2_model *Model)
{
FILE *File = fopen (FilePath, "rb");
if (!File)
{
fprintf (stderr, "Error: couldn't open \"%s\"!\n", FilePath);
return;
}
#define FREAD(Dest, Type, Count)\
fread(Dest, sizeof(Type), Count, File)
#define FSEEK(Offset)\
fseek(File, Offset, SEEK_SET)
#define ALLOC(Type, Count)\
(Type *)malloc(sizeof(Type) * Count)
/* Read Header */
FREAD(&Model->Header, md2_header, 1);
if ((Model->Header.Magic != 844121161) ||
(Model->Header.Version != 8))
{
fprintf (stderr, "Error: bad md2 Version or identifier\n");
fclose (File);
return;
}
/* Memory allocations */
Model->Skins = ALLOC(md2_skin, Model->Header.NumTextures);
Model->UVs = ALLOC(md2_uv, Model->Header.NumUVs);
Model->Triangles = ALLOC(md2_triangle, Model->Header.NumTrigs);
Model->Frames = ALLOC(md2_frame, Model->Header.NumFrames);
Model->GLCommands = ALLOC(int, Model->Header.NumGLCommands);
/* Read model data */
FSEEK(Model->Header.OffsetTextures);
FREAD(Model->Skins, md2_skin, Model->Header.NumTextures);
FSEEK(Model->Header.OffsetUVs);
FREAD(Model->UVs, md2_uv, Model->Header.NumUVs);
FSEEK(Model->Header.OffsetTrigs);
FREAD(Model->Triangles, md2_triangle, Model->Header.NumTrigs);
FSEEK(Model->Header.OffsetGLCommands);
FREAD(Model->GLCommands, int, Model->Header.NumGLCommands);
/* Read frames */
FSEEK(Model->Header.OffsetFrames);
for (int i = 0; i < Model->Header.NumFrames; i++)
{
/* Memory allocation for vertices of this frame */
Model->Frames[i].Vertices = (md2_vertex *)
malloc(sizeof(md2_vertex) * Model->Header.NumVertices);
/* Read frame data */
FREAD(&Model->Frames[i].Scale, v3, 1);
FREAD(&Model->Frames[i].Translate, v3, 1);
FREAD(Model->Frames[i].Name, char, 16);
FREAD(Model->Frames[i].Vertices, md2_vertex, Model->Header.NumVertices);
}
v3 *Vertices = ALLOC(v3, Model->Header.NumVertices);
md2_frame *Frame = &Model->Frames[0];
For(u32, i, Model->Header.NumVertices)
{
Vertices[i] = V3(
(Frame->Vertices[i].At[0] * Frame->Scale[0]) + Frame->Translate[0],
(Frame->Vertices[i].At[1] * Frame->Scale[1]) + Frame->Translate[1],
(Frame->Vertices[i].At[2] * Frame->Scale[2]) + Frame->Translate[2]);
}
glGenBuffers(1, &Model->VBO);
glBindBuffer(GL_ARRAY_BUFFER, Model->VBO);
glBufferData(GL_ARRAY_BUFFER, Model->Header.NumVertices * sizeof(v3), Vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &Model->VAO);
glBindVertexArray(Model->VAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
fclose (File);
free(Vertices);
#undef FSEEK
#undef FREAD
#undef ALLOC
}
Only passing the vertices data. Which, from my understanding Header->NumVertices is the number of vertices in each frame. So I'm taking an arbitrary frame (frame 0 in this case) and reading its uncompressed vertices data into Vertices.
Now I read in a book that Quake had their y and z axes flipped, but that still didn't change much.
Here's how I'm drawing the model:
GLuint Shader = Data->Shaders.Md2Test;
ShaderUse(Shader);
ShaderSetM4(Shader, "view", &WorldToView);
ShaderSetM4(Shader, "projection", &ViewToProjection);
glBindVertexArray(DrFreak.VAO);
{
ModelToWorld = m4_Identity;
ShaderSetM4(Shader, "model", &ModelToWorld);
glDrawArrays(GL_TRIANGLES, 0, DrFreak.Header.NumVertices);
}
glBindVertexArray(0);
The matrices are calculated in a CameraUpdate function which I can verify is working correctly because everything else in the scene render properly except the MD2 model. See:
Everything in yellow is supposed to be the MD2 model.
Here are my shaders (pretty much the same shaders for the crates and planes except there's only one 'in' variable, the position and no UVs):
#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
}
#version 330 core
out vec4 color;
void main()
{
color = vec4(1, 1, 0, 1);
}
I've been stuck here for a couple of days. I stepped into the loading code and I seem to be getting valid values. I'm not sure what's the issue. What am I doing wrong/missing?
Any help is appreciated.
I fixed the problem by duplicating the vertices/uvs getting them from the tirangles data. I didn't have to flip the 't' UV coordinate like many tutorials do. I switched the y and z coordinates cause they're flipped.
u32 NumVerts = Model->Header.NumTrigs * 3;
u32 NumUVs = NumVerts;
v3 *Vertices = ALLOC(v3, NumVerts);
v2 *UVs = ALLOC(v2, NumUVs);
md2_frame *Frame = &Model->Frames[0]; // render first frame for testing
For(u32, i, Model->Header.NumTrigs)
{
For(u32, j, 3)
{
u32 VertIndex = Model->Triangles[i].Vertices[j];
Vertices[i * 3 + j] = V3(
(Frame->Vertices[VertIndex].At[0] * Frame->Scale[0]) + Frame->Translate[0],
(Frame->Vertices[VertIndex].At[2] * Frame->Scale[2]) + Frame->Translate[2],
(Frame->Vertices[VertIndex].At[1] * Frame->Scale[1]) + Frame->Translate[1]);
u32 UVIndex = Model->Triangles[i].UVs[j];
UVs[i * 3 + j] = V2(
Model->UVs[UVIndex].u / (r32)Model->Header.TextureWidth,
Model->UVs[UVIndex].v / (r32)Model->Header.TextureHeight);
}
}
glGenVertexArrays(1, &Model->VAO);
glBindVertexArray(Model->VAO);
glGenBuffers(1, &Model->VBO);
glBindBuffer(GL_ARRAY_BUFFER, Model->VBO);
glBufferData(GL_ARRAY_BUFFER, NumVerts * sizeof(v3), Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
u32 UVBuffer;
glGenBuffers(1, &UVBuffer);
glBindBuffer(GL_ARRAY_BUFFER, UVBuffer);
glBufferData(GL_ARRAY_BUFFER, NumUVs * sizeof(v2), UVs, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
I will probably use indexed arrays and glDrawElements. But for my testing purposes glDrawArrays is good enough. If anyone knows of a better way to do all this feel free to leave a comment.
And there's Dr Freak chillin'
I am trying to create a set of gstreamer plugins for image processing routines. I have successfully created a source element that reads the image and the metadata into a GstBuffer, and a sink element that writes the data in the buffer to disk (along with the accompanying metadata) as desired. I have successfully tested these, and achieve the desired output (identical to the input with no filters in place).
I have also created a stretching element, that utilizes an external library to fill the dynamic range available (ie a 16-bit image with only 12-bits used per pixel can be stretched to fill the whole 16-bits available).
If I simply push the unchanged buffer out on the srcpad for the Stretching element, I get what I would expect (an unchanged image). However, if I try to perform any sort of manipulation of the data in the buffer, the data in the buffer is set to 0's.
Here is the current implementation of the chain() function for my Stretching plugin:
static GstFlowReturn
gst_stretching_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
{
GstStretching *filter;
filter = GST_STRETCHING(parent);
g_print("Stretching...\n");
guint num_rows;
g_object_get(G_OBJECT(parent), "num_rows", &num_rows, NULL);
guint num_cols;
g_object_get(G_OBJECT(parent), "num_cols", &num_cols, NULL);
guint bit_depth;
g_object_get(G_OBJECT(parent), "bit_depth", &bit_depth, NULL);
guint sig_bits;
g_object_get(G_OBJECT(parent), "sig_bits", &sig_bits, NULL);
gchar *product;
g_object_get(G_OBJECT(parent), "product", &product, NULL);
GstMapInfo info_in;
gst_buffer_map(buf, &info_in, GST_MAP_WRITE);
guint8 *in = info_in.data;
GstMemory *mem;
mem = gst_allocator_alloc(NULL, num_rows*num_cols*bit_depth/8, NULL);
GstMapInfo info_out;
gst_memory_map(mem, &info_out, GST_MAP_WRITE);
guint8 *out = info_out.data;
float *rad_gain[4] = {NULL, NULL, NULL, NULL};
float *rad_offset[4] = {NULL, NULL, NULL, NULL};
StretchingImage((unsigned short int *)in, num_rows, num_cols, sig_bits,
bit_depth, rad_gain, rad_offset, 0, product, (unsigned short int *)out);
gst_buffer_unmap(buf, &info_in);
gst_buffer_replace_all_memory(buf, mem);
return gst_pad_push(filter->srcpad, buf);
}
When this did not work, I also tried a simple change of the data manually (to see if I would get the expected output):
static GstFlowReturn
gst_stretching_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
{
GstStretching *filter;
filter = GST_STRETCHING(parent);
g_print("Stretching...\n");
guint num_rows;
g_object_get(G_OBJECT(parent), "num_rows", &num_rows, NULL);
guint num_cols;
g_object_get(G_OBJECT(parent), "num_cols", &num_cols, NULL);
guint bit_depth;
g_object_get(G_OBJECT(parent), "bit_depth", &bit_depth, NULL);
guint sig_bits;
g_object_get(G_OBJECT(parent), "sig_bits", &sig_bits, NULL);
gchar *product;
g_object_get(G_OBJECT(parent), "product", &product, NULL);
GstMapInfo info_in;
gst_buffer_map(buf, &info_in, GST_MAP_WRITE);
guint8 *in = info_in.data;
GstMemory *mem;
mem = gst_allocator_alloc(NULL, num_rows*num_cols*bit_depth/8, NULL);
GstMapInfo info_out;
gst_memory_map(mem, &info_out, GST_MAP_WRITE);
guint8 *out = info_out.data;
int i;
for (i=0; i<num_rows*num_cols*bit_depth/8; i++) {
out[i] = 255;
}
float *rad_gain[4] = {NULL, NULL, NULL, NULL};
float *rad_offset[4] = {NULL, NULL, NULL, NULL};
StretchingImage((unsigned short int *)in, num_rows, num_cols, sig_bits,
bit_depth, rad_gain, rad_offset, 0, product, (unsigned short int *)out);
gst_buffer_unmap(buf, &info_in);
gst_buffer_replace_all_memory(buf, mem);
return gst_pad_push(filter->srcpad, buf);
}
Even with this, I still obtain all 0's when I examine the output. I am assuming I am doing something wrong when trying to access the data in the buffer, but haven't yet been able to figure out what it may be. Any ideas?
The
gst_buffer_map(buf, &info_in, GST_MAP_WRITE);
should be
gst_buffer_map(buf, &info_in, GST_MAP_READ);
Also fyi, you can simplfy the code
guint num_rows, num_cols, ...;
g_object_get(G_OBJECT(parent),
"num_rows", &num_rows,
"num_cols", &num_cols,
...
NULL);