diff --git a/scene-graph-example.scg b/scene-graph-example.scg new file mode 100644 index 0000000..07c356d --- /dev/null +++ b/scene-graph-example.scg @@ -0,0 +1,84 @@ +# Kommentar + +# Definition eines Modells +# Model hat eindeutige numerische ID +# Durch diese ID kann ein Objekt dieses Modell verwenden +model 0 + +# Angabe der OBJ Datei +file ../obj/cube.obj + +# Angabe der Textur Datei +texture ../texture/crate.png + + +# Definition eines Modells oder Objekts endet automatisch +# bei beginn einer neuen Definition + +model 1 +file ../obj/earth.obj +texture ../texture/pb.png + + +# Definition eines Objekts +# jedes Objekt braucht eindeutige numerische ID +obj 0 + +# Angabe welches Modell das Objekt nutzt +# Kann im Fall von Gruppenobjekten weggelassen werden +use 0 + +# lesbarer Name des Objekts +# eigentlich nur für debugging, muss wahrscheinlich +# garnicht implementiert werden +name myCrate1 + +# Position des Objekts +# relativ zur Position des Parent Elements +position 0.0 0.0 2.0 + +# Skalierung des Objekts +# relativ zur Skalierung des Parent Elements +scale 1.0 1.0 1.0 + + +# rotationen +rotationX 0.0 +rotationY 1.5 +rotationZ 0.0 + + + +obj 1 +use 0 +name myCrate2 +position 0.0 1.0 0.0 +scale 0.5 0.5 0.5 +# Definition des Parent Objekts +parent 0 + + +obj 2 +use 1 +name Sonne +#texture ../texture/pb.png +position 0.0 0.0 0.0 +scale 2.0 2.0 2.0 + + +obj 3 +use 1 +name Erde +#texture ../texture/earth/day.png +position 4.0 0.0 0.0 +scale 1.0 1.0 1.0 +parent 2 + + +obj 4 +use 1 +name Mond +#texture ../texture/checkerboard.png +position 2.0 0.0 0.0 +scale 0.5 0.5 0.5 +parent 3 \ No newline at end of file diff --git a/src/main.c b/src/main.c index 7f1d2b7..55ad673 100644 --- a/src/main.c +++ b/src/main.c @@ -12,9 +12,6 @@ #include "wavefrontobj.h" #include "sceneGraph.h" -#define STB_IMAGE_IMPLEMENTATION -#include "../lib/stb_image.h" - #include #include #include @@ -32,17 +29,6 @@ GLuint program; int flipFlag = 1; -GLuint textures[NUM_TEXTURES]; -char* textureFiles[NUM_TEXTURES] = { - "../texture/earth/day.png", - "../texture/earth/night.png", - "../texture/earth/clouds.png", - "../texture/earth/ocean_mask.png", - "../texture/earth/normal.png" -}; - -ObjectData* objectData; - bool exitRequested = false; GLFWwindow* window; @@ -65,15 +51,6 @@ mat4 viewingTransformation; // Define a global scene graph root node SceneNode* rootNode; - -int numModels = 0; -char* models[] = { - "../obj/Xblock.obj", - "../obj/Yblock.obj", - "../obj/Yblock_rotated.obj", - "../obj/Zblock.obj", -}; - /** * Input handler for camera movement. * */ @@ -126,38 +103,9 @@ void keyboardHandler(GLFWwindow* window, int key, int scancode, int action, int } } -/** - * Loads textures. - */ -void loadTexture(char* textureFile, GLuint* texture) { - int width, height, nrChannels; - unsigned char* image = stbi_load(textureFile, &width, &height, &nrChannels, 0); - - // default: 3 channels, RGB - - GLenum channelFormats[] = { - 0, - GL_RED, - GL_RG, - GL_RGB, - GL_RGBA - }; - GLenum format = channelFormats[nrChannels]; - - glGenTextures(1, texture); - glBindTexture(GL_TEXTURE_2D, *texture); - - printf("%s - %d\n", textureFile, nrChannels); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, GL_UNSIGNED_BYTE, image); - // load texture using previously determined format ----- ^^^^^^ - - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - - stbi_image_free(image); -} - void renderNode(SceneNode* node) { + if (!node->model) return; + mat4 modelView; identity(&modelView); multiply(&modelView, &node->worldTransformation, &modelView); @@ -182,14 +130,14 @@ void renderNode(SceneNode* node) { textureLocation = glGetUniformLocation(program, "textureSampler"); glUniform1i(textureLocation, 0); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, textures[DAY]); + glBindTexture(GL_TEXTURE_2D, node->model->texture); - textureLocation = glGetUniformLocation(program, "normalMap"); - glUniform1i(textureLocation, 4); - glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, textures[NORMAL]); + // textureLocation = glGetUniformLocation(program, "normalMap"); + // glUniform1i(textureLocation, 4); + // glActiveTexture(GL_TEXTURE4); + // glBindTexture(GL_TEXTURE_2D, textures[NORMAL]); - draw_object(node->objectData); + draw_object(node->model->objectData); } void init(void) { @@ -260,30 +208,11 @@ void init(void) { } - // --------------- READ MODEL FILES - - //objectData = readObjFiles(&models, numModels); - char* c = "../obj/new/Window.obj"; - objectData = readSingleObjFile(&c); - - stbi_set_flip_vertically_on_load(flipFlag); - // -------------- READ TEXTURE FILES - for (int i = 0; i < NUM_TEXTURES; i++) { - loadTexture(textureFiles[i], &textures[i]); - } - + // --------------- READ SCENE GRAPH setNodeRenderFunction(&renderNode); - // Create the root scene node - rootNode = createSceneNode(); - - // Create a child node (e.g., for the model) - SceneNode* modelNode = createSceneNode(); - modelNode->objectData = objectData; - addChild(rootNode, modelNode); - - // Set transformations for the modelNode (example) - rotateY(&modelNode->transformation, &modelNode->transformation, pi / 4); + // read scene graph + rootNode = loadSceneGraphFromFile("../scene-graph-example.scg"); // ENABLE BACKFACE CULLING glFrontFace(GL_CCW); @@ -361,17 +290,10 @@ void draw(void) { glUniform4f(glGetUniformLocation(program, "lightColor"), 1.0f, 1.0f, 1.0f, 1.0f); glUniform4f(glGetUniformLocation(program, "ambientLight"), 0.05f, 0.05f, 0.05f, 1.0f); - // BIND TEXTURES - GLuint textureLocation; - textureLocation = glGetUniformLocation(program, "textureSampler"); - glUniform1i(textureLocation, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, textures[DAY]); + vec4 lightPosition = {cos(stepi) * 5.0f, 5.0f, sin(stepi) * 5.0f, 1.0f}; + multiplyAny(&lightPosition, &viewingTransformation, &lightPosition, 4, 4, 1); - textureLocation = glGetUniformLocation(program, "normalMap"); - glUniform1i(textureLocation, 4); - glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, textures[NORMAL]); + glUniform3f(glGetUniformLocation(program, "lightPosition"), lightPosition.x, lightPosition.y, lightPosition.z); renderSceneNode(rootNode); diff --git a/src/objectHandler.c b/src/objectHandler.c index 7178283..8e02d65 100644 --- a/src/objectHandler.c +++ b/src/objectHandler.c @@ -94,7 +94,7 @@ ObjectData* readObjFiles(char** path, int numModels, int* count) { * Takes a single object and reads it a certain number of times. * Returns an array of objects. */ -ObjectData* readSingleObjFile(char** path) { +ObjectData* readSingleObjFile(char* path) { ObjectData* objectData = (ObjectData*) malloc(sizeof(ObjectData)); if (!objectData) { @@ -103,7 +103,7 @@ ObjectData* readSingleObjFile(char** path) { return NULL; } - objectData->object = readObjFile(*path); + objectData->object = readObjFile(path); load_object(objectData); return objectData; diff --git a/src/objectHandler.h b/src/objectHandler.h index 2f7d46c..efcf724 100644 --- a/src/objectHandler.h +++ b/src/objectHandler.h @@ -11,7 +11,7 @@ typedef struct { extern void load_object(ObjectData* objectData); extern ObjectData* readObjFiles(char** path, int numModels, int* count); -extern ObjectData* readSingleObjFile(char** path); +extern ObjectData* readSingleObjFile(char* path); extern void draw_object(ObjectData* objectData); #endif \ No newline at end of file diff --git a/src/sceneGraph.c b/src/sceneGraph.c index 36a00d7..d551b51 100644 --- a/src/sceneGraph.c +++ b/src/sceneGraph.c @@ -3,6 +3,16 @@ #include "sceneGraph.h" #include "objectHandler.h" #include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "../lib/stb_image.h" + +#define SCG_LINE_BUFFER_SIZE 1024 +#define SCG_KEYWORD_BUFFER_SIZE 32 void (*renderFunction)(SceneNode*); @@ -10,13 +20,15 @@ void setNodeRenderFunction(void (*newRenderFunction)(SceneNode*)) { renderFunction = newRenderFunction; } -SceneNode* createSceneNode() { +SceneNode* createSceneNode(int id) { SceneNode* node = (SceneNode*)malloc(sizeof(SceneNode)); identity(&node->transformation); identity(&node->worldTransformation); + node->id = id; node->children = NULL; node->numChildren = 0; - node->objectData = NULL; + node->model = NULL; + node->name = NULL; return node; } @@ -34,7 +46,7 @@ void updateSceneNode(SceneNode* node, mat4* parentTransformation) { } void renderSceneNode(SceneNode* node) { - if (node->objectData) { + if (node->model) { renderFunction(node); } for (int i = 0; i < node->numChildren; i++) { @@ -48,4 +60,259 @@ void freeSceneNode(SceneNode* node) { } free(node->children); free(node); +} + +void loadTexture(char* textureFile, GLuint* texture) { + int width, height, nrChannels; + unsigned char* image = stbi_load(textureFile, &width, &height, &nrChannels, 0); + + // default: 3 channels, RGB + + GLenum channelFormats[] = { + 0, + GL_RED, + GL_RG, + GL_RGB, + GL_RGBA + }; + GLenum format = channelFormats[nrChannels]; + + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + + printf("%s - %d\n", textureFile, nrChannels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, GL_UNSIGNED_BYTE, image); + // load texture using previously determined format ----- ^^^^^^ + + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + stbi_image_free(image); +} + + +SceneNode* findNode(int id, SceneNode* root) { + if (root->id == id) return root; + for (int i = 0; i < root->numChildren; i++) { + SceneNode* node = findNode(id, root->children[i]); + if (node) return node; + } + return NULL; +} + +void printSceneGraph(SceneNode* node, int level) { + for (int i = 0; i < level; i++) { + printf(" - "); + } + if (node->name) { + printf("%s\n", node->name); + } else { + printf("%d\n", node->id); + } + for (int i = 0; i < node->numChildren; i++) { + printSceneGraph(node->children[i], level + 1); + } +} + +SceneNode* loadSceneGraphFromFile(char* path) { + FILE* fp = fopen(path, "r"); + if (fp == NULL) { + fprintf(stderr, "File could not be opened: %s\n", path); + } + + char buf[SCG_LINE_BUFFER_SIZE]; + char keyword[SCG_KEYWORD_BUFFER_SIZE]; + + // generate keyword buffer format string using SCG_KEYWORD_BUFFER_SIZE + // when SCK_KEYWORD_BUFFER_SIZE is 32 keywordBufferFormat will contain "%32s" + // avoid buffer overflow while reading buffer while also allowing for dynamic buffer scaling + // there is probably a better solution for this but I couldn't find one :( + char* keywordBufferFormat = (char*)malloc(sizeof(char) * (4 + (int)log10((double)SCG_KEYWORD_BUFFER_SIZE))); + sprintf(keywordBufferFormat, "%%%ds", SCG_KEYWORD_BUFFER_SIZE); + + SceneNode* root = createSceneNode(-1); + root->name = "ROOT"; + + int maxModelId = 0; + int maxObjId = 0; + + while (fgets(buf, SCG_LINE_BUFFER_SIZE, fp)) { + if (buf[0] == '#') continue; + sscanf(buf, keywordBufferFormat, keyword); + // printf("%s\n", keyword); + + if (strcmp(keyword, "model") == 0) { + int modelId = 0; + sscanf(buf, "model %d", &modelId); + if (modelId > maxModelId) { + maxModelId = modelId; + } + } + } + + Model* models = (Model*)malloc(sizeof(Model) * (maxModelId + 1)); + + int currentModel = 0; + + SceneNode* currentNode = NULL; + bool currentNodeHasParent = false; + + char filepathBuffer[1024]; + + rewind(fp); + int currentLine = 0; + while (fgets(buf, SCG_LINE_BUFFER_SIZE, fp)) { + currentLine++; + if (buf[0] == '#') continue; + if (buf[0] == '\r' || buf[0] == '\n' || buf[0] == '\0') continue; + sscanf(buf, keywordBufferFormat, keyword); + + if (strcmp(keyword, "model") == 0) { + sscanf(buf, "model %d", ¤tModel); + continue; + } + if (strcmp(keyword, "file") == 0) { + sscanf(buf, "file %s", filepathBuffer); + models[currentModel].objectData = readSingleObjFile(filepathBuffer); + continue; + } + if (strcmp(keyword, "texture") == 0) { + sscanf(buf, "texture %s", filepathBuffer); + loadTexture(filepathBuffer, &models[currentModel].texture); + continue; + } + + + if (strcmp(keyword, "obj") == 0) { + + if (currentNode && !currentNodeHasParent) { + addChild(root, currentNode); + } + + currentNode = NULL; + currentNodeHasParent = false; + + int nodeId = 0; + sscanf(buf, "obj %d", &nodeId); + if (findNode(nodeId, root)) { + fprintf(stderr, "redeclaration of objet with id %d - line %d\n", nodeId, currentLine); + return NULL; + } + currentNode = createSceneNode(nodeId); + continue; + } + if (strcmp(keyword, "name") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected, can't assign name - line %d\n", currentLine); + return NULL; + } + sscanf(buf, "name %s", filepathBuffer); + char* name = (char*)malloc(strlen(filepathBuffer) * sizeof(char) + 1); + strcpy(name, filepathBuffer); + currentNode->name = name; + continue; + } + + if (strcmp(keyword, "parent") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected, can't assign parent - line %d\n", currentLine); + return NULL; + } + if (currentNodeHasParent) { + fprintf(stderr, "selected node already has parent - line %d\n", currentLine); + return NULL; + } + + int parentId = 0; + sscanf(buf, "parent %d", &parentId); + + SceneNode* parent = findNode(parentId, root); + if (!parent) { + fprintf(stderr, "parent node with id %d not found - line %d\n", parentId, currentLine); + return NULL; + } + + addChild(parent, currentNode); + currentNodeHasParent = true; + + continue; + } + + if (strcmp(keyword, "use") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected to assign model to - line %d\n", currentLine); + return NULL; + } + + int usedModel = 0; + sscanf(buf, "use %d", &usedModel); + + if (usedModel > maxModelId || usedModel < 0) { + fprintf(stderr, "model with id %d not found - line %d\n", usedModel, currentLine); + return NULL; + } + + currentNode->model = &models[usedModel]; + } + + if (strcmp(keyword, "position") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected to assign position to - line %d\n", currentLine); + return NULL; + } + vec3 translation; + sscanf(buf, "position %f %f %f", &translation.x, &translation.y, &translation.z); + translate(¤tNode->transformation, ¤tNode->transformation, &translation); + } + + if (strcmp(keyword, "scale") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected to assign scale to - line %d\n", currentLine); + return NULL; + } + vec3 translation; + sscanf(buf, "scale %f %f %f", &translation.x, &translation.y, &translation.z); + scale(¤tNode->transformation, ¤tNode->transformation, &translation); + } + + if (strcmp(keyword, "rotationX") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected to assign rotationX to - line %d\n", currentLine); + return NULL; + } + GLfloat angle; + sscanf(buf, "rotationX %f", &angle); + rotateX(¤tNode->transformation, ¤tNode->transformation, angle); + } + if (strcmp(keyword, "rotationY") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected to assign rotationY to - line %d\n", currentLine); + return NULL; + } + GLfloat angle; + sscanf(buf, "rotationY %f", &angle); + rotateY(¤tNode->transformation, ¤tNode->transformation, angle); + } + if (strcmp(keyword, "rotationZ") == 0) { + if (!currentNode) { + fprintf(stderr, "no node selected to assign rotationZ to - line %d\n", currentLine); + return NULL; + } + GLfloat angle; + sscanf(buf, "rotationZ %f", &angle); + rotateZ(¤tNode->transformation, ¤tNode->transformation, angle); + } + } + + if (currentNode && !currentNodeHasParent) { + addChild(root, currentNode); + } + + mat4 rootTransform; + identity(&rootTransform); + updateSceneNode(root, &rootTransform); + + printSceneGraph(root, 0); + + return root; } \ No newline at end of file diff --git a/src/sceneGraph.h b/src/sceneGraph.h index 066977f..97ab5ec 100644 --- a/src/sceneGraph.h +++ b/src/sceneGraph.h @@ -11,20 +11,27 @@ typedef struct SceneNode SceneNode; +typedef struct { + ObjectData* objectData; + GLuint texture; +} Model; + struct SceneNode { + int id; mat4 transformation; // Local transformation matrix mat4 worldTransformation; // World transformation matrix SceneNode** children; // Array of pointers to child nodes int numChildren; // Number of child nodes - ObjectData* objectData; - + Model* model; + char* name; }; -void setNodeRenderFunction(void (*newRenderFunction)(SceneNode*)); +extern void setNodeRenderFunction(void (*newRenderFunction)(SceneNode*)); extern SceneNode* createSceneNode(); extern void addChild(SceneNode* parent, SceneNode* child); extern void updateSceneNode(SceneNode* node, mat4* parentTransformation); extern void renderSceneNode(SceneNode* node); extern void freeSceneNode(SceneNode* node); +extern SceneNode* loadSceneGraphFromFile(char* path); #endif \ No newline at end of file diff --git a/test-scene-graph.scg b/test-scene-graph.scg new file mode 100644 index 0000000..c089f07 --- /dev/null +++ b/test-scene-graph.scg @@ -0,0 +1,26 @@ +model 0 +file ../obj/cube.obj +texture ../texture/crate/texture.jpg + +obj 0 +use 0 +scale 0.1 1.0 0.1 +name box1 + +obj 1 +parent 0 +scale 0.2 0.8 0.2 +position 0.5 0.0 0.0 +name box2 + +obj 2 +parent 1 +scale 0.3 0.6 0.3 +position 0.5 0.0 0.0 +name box3 + +obj 3 +parent 2 +scale 0.4 0.4 0.4 +position 0.5 0.0 0.0 +name box4 \ No newline at end of file