[OpenGL & C]Modelldarstellung mit Fehlern (OBJ-Loader)
-
Screenshot: https://i.imgur.com/CaDAA1J.png
Egal welches Modell ich lade, es kommt immer mit Fehldarstellungen (s. Screenshot). Ich vermute, dass es an glBufferData, glVertexAttribPointer oder glDrawElements liegt, weil evtl. die Bytegrößen nicht richtig gesetzt worden sind.
Ich verwende eine einfach verkette Listen beim Parsen des Objekts (ist sehr langsam). Zudem verwende ich benutzdefinierte Array-Objekte/Struktur (die noch zusätzlich zum Array die Größeninformation inne hat). Das wäre einmal FloatArray und UIntArray. FloatArray ist ähnlich wie UIntArray mittels typedef definiert. Der Unterschied ist, dass das UIntArray ein uint32_t Array beinhaltet. Hier ist die sind die typedefs dazu:
#ifndef ARRAYS_H_ #define ARRAYS_H_ #include <stddef.h> #include <inttypes.h> // uint array object typedef struct { uint32_t* elements; size_t length; } UIntArray; // float array object typedef struct { float* elements; size_t length; } FloatArray; // Dumps an primitive uint array on the heap with its size info UIntArray* newUIntArray(uint32_t* dump, const size_t length); // Dumps an primitive uint array on the heap with its size info UIntArray* newUIntArrayNoDump(const size_t length); // Creates a float array object with the specified size FloatArray* newFloatArrayNoDump(const size_t length); // Dumps an primitive float array on the heap with its size info FloatArray* newFloatArray(float* dump, const size_t length); // Free up used heap memory by the uint array object void delUIntArray(UIntArray* p); // Free up used heap memory by the float array object void delFloatArray(FloatArray* p); #endif // ARRAYS_H_
Details
Das ist die engine.c mit der Funktion run, die die Rendering-Loop enthält:
#include "engine.h" #include "shader.h" #include "mesh.h" #include "linalg.h" #include <math.h> #include "arrays.h" // TODO define a function instead of a macro #define TO_RAD(d) ((float) (d * M_PI) / 180.0f) void run() { // Window creation WNDHND wh = createWindow(WIN_WIDTH, WIN_HEIGHT, WIN_TITLE); // Compile and link the shaders SHADERID progID = setupShaders("vs1.glsl", "fs1.glsl"); // Dump vertex and index data into the array objects from an OBJ file FloatArray* vertexDump; UIntArray* indexDump; loadOBJ("cow.smf", &vertexDump, &indexDump); // Send the vertex and index data to the graphics driver OBJECTID vao1 = setupBuffers(vertexDump, indexDump); // Model matrix definition (col.-major) const float Tx = +0.0f; const float Ty = +0.0f; const float Tz = +1.0f; Matrix16f M = { +1.0f, +0.0f, +0.0f, +Tx, +0.0f, +1.0f, +0.0f, +Ty, +0.0f, +0.0f, +1.0f, +Tz, +0.0f, +0.0f, +0.0f, +1.0f, }; // View matrix definition (col.-major) const float Rx = 1.0f; const float Ry = 0.0f; const float Rz = 0.0f; const float Ux = 0.0f; const float Uy = 1.0f; const float Uz = 0.0f; const float Px = 0.0f; const float Py = 0.0f; const float Pz = 0.0f; const float Fx = 0.0f; const float Fy = 0.0f; const float Fz = 1.0f; Matrix16f V = { +Rx, +Ry, +Rz, -Px, +Ux, +Uy, -Uz, -Py, -Fx, -Fy, -Fz, -Pz, +0.0f, +0.0f, +0.0f, +1.0f, }; // Projection matrix (col.-major) const float near = 0.1f; const float far = 100.0f; const float aspect = (float) WIN_WIDTH / WIN_HEIGHT; const float fov = 75.0f; const float range = tan(TO_RAD(fov) / 2.0f) * near; const float Sx = (float) (2.0f * near) / (float) (range * aspect + range * aspect); const float Sy = (float) near / (float) range; const float Sz = (float) -(far + near) / (float) (far - near); const float PPz = (float) -(2 * far * near) / (float) (far - near); Matrix16f P = { +Sx, +0.0f, +0.0f, +0.0f, +0.0f, +Sy, +0.0f, +0.0f, +0.0f, +0.0f, +Sz, +PPz, +0.0f, +0.0f, -1.0f, +0.0f, }; // Construct MVP matrix Matrix16f MVP = {0}; multiplyMatrices(&MVP, M, V); multiplyMatrices(&MVP, MVP, P); // Send the constructed MVP matrix to the vertex shader sendMVPMatrix(progID, MVP.entries); // Set the clear color const float r = 0.3f; const float g = 0.3f; const float b = 0.3f; const float a = 1.0f; glClearColor(r, g, b, a); // Enable depth glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // Culling options glDisable(GL_CULL_FACE); glFrontFace(GL_CCW); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Render loop while(!windowShouldClose(wh)) { // Clear color buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Resize frame buffer if the window dimensions are changing maintainAspectRatio(wh); // Use shader program glUseProgram(progID); // Activate the VAO glBindVertexArray(vao1); // Enable the position attribute in the vertex shader at location 0 glEnableVertexAttribArray(0); // Draw const uint32_t faceCount = indexDump->length / 3; glDrawElements(GL_TRIANGLES, faceCount, GL_UNSIGNED_INT, 0); // Swap buffers swapBuffers(wh); // Listen for any events pollEvents(); } destroyWindow(wh); }
Das ist mesh.c mit den Funktionen setupBuffers und loadOBJ:
#include "mesh.h" #include "shader.h" #include <string.h> BUFFERID setupBuffers(FloatArray* verts, UIntArray* inds) { // Generate VAO uint32_t vao; glGenVertexArrays(1, &vao); // Bind the VAO glBindVertexArray(vao); // Generate VBO uint32_t vbo; glGenBuffers(1, &vbo); // Bind VBO glBindBuffer(GL_ARRAY_BUFFER, vbo); // Feed VBO with data const uint32_t arrayBufferSize = sizeof(float) * verts->length; glBufferData(GL_ARRAY_BUFFER, arrayBufferSize, verts->elements, GL_STATIC_DRAW); // TODO remove debug output // DEBUG vertices fprintf(stdout, "DEBUG verts->length in function setupBuffers: %d\n", verts->length); for (int i = 0; i < verts->length; ++i) { fprintf(stdout, "DEBUG verts->elements[%d] in function setupBuffers: %f\n", i, verts->elements[i]); } // Link the position attribute in the vertex shader with the data at // attribute location 0. const uint32_t componentCountPos = 3; const uint32_t stride = sizeof(float) * componentCountPos; const uint32_t offsetPos = 0; glVertexAttribPointer(0, componentCountPos, GL_FLOAT, GL_FALSE, stride, (void*) offsetPos); glEnableVertexAttribArray(0); // Generate element buffer object uint32_t ebo; glGenBuffers(1, &ebo); // Bind the EBO glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); // Feed with index data const uint32_t elementArrayBufferSize = sizeof(uint32_t) * inds->length; glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementArrayBufferSize, inds->elements, GL_STATIC_DRAW); // TODO remove debug output // DEBUG indices fprintf(stdout, "DEBUG inds->length in function setupBuffers: %d\n", inds->length); for (int i = 0; i < inds->length; ++i) { fprintf(stdout, "DEBUG inds->elements[%d] in function setupBuffers: %d\n", i, inds->elements[i]); } // Return back the ID to the VAO return vao; } // TODO complete implementation: read UVs, normals too void loadOBJ(const uint8_t* path, FloatArray** verts, UIntArray** inds) { // Open OBJ file FILE* fis = fopen(path, "rb"); if (!fis) { perror("Error: failed to open OBJ file.\n"); exit(1); } // Parse vertex data FloatNode* fn = NULL; UIntNode* uin = NULL; uint8_t lineHeader[128]; float vX = 0.0f; float vY = 0.0f; float vZ = 0.0f; uint32_t i1 = 0; uint32_t i2 = 0; uint32_t i3 = 0; while (fscanf(fis, "%s", lineHeader) != EOF) { if (strcmp(lineHeader, "v") == 0) { if (fscanf(fis, "%f %f %f\n", &vX, &vY, &vZ)) { fn = addFloatNodeLast(fn, vX); fn = addFloatNodeLast(fn, vY); fn = addFloatNodeLast(fn, vZ); } } else if (strcmp(lineHeader, "f") == 0) { uint32_t junk; if (fscanf(fis, "%d %d %d %d/%d/%d %d/%d/%d\n", &i1, &i2, &i3, &junk, &junk, &junk, &junk, &junk, &junk)) { uin = addUIntNodeLast(uin, i1); uin = addUIntNodeLast(uin, i2); uin = addUIntNodeLast(uin, i3); } } } // Copy float list to float array object const uint64_t flLength = getFloatListLength(fn); FloatNode* fnIt = fn; (*verts) = newFloatArrayNoDump(flLength); for (uint64_t i = 0; i < flLength; ++i) { (*verts)->elements[i] = fnIt->data; if (fnIt) { fnIt = fnIt->next; } } // Copy uint32_t list to uint32_t array object const uint64_t uilLength = getUIntListLength(uin); UIntNode* uinIt = uin; (*inds) = newUIntArrayNoDump(uilLength); for (uint64_t i = 0; i < (*inds)->length; ++i) { (*inds)->elements[i] = uinIt->data; if (uinIt) { uinIt = uinIt->next; } } // Close file stream fclose(fis); } // end of loadOBJ
Das ist cow.smf (das Modell, das geladen wird): https://pastebin.com/Fg3hMrQH
-
Edit: Ansonsten werden die Vertex- und Indexdaten schon korrekt geparst.