read scene graph from file

This commit is contained in:
Luca Conte 2024-06-19 17:19:20 +02:00
parent 3c0b9f42c1
commit 70c611d0d6
7 changed files with 407 additions and 101 deletions

84
scene-graph-example.scg Normal file
View File

@ -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

View File

@ -12,9 +12,6 @@
#include "wavefrontobj.h" #include "wavefrontobj.h"
#include "sceneGraph.h" #include "sceneGraph.h"
#define STB_IMAGE_IMPLEMENTATION
#include "../lib/stb_image.h"
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
@ -32,17 +29,6 @@ GLuint program;
int flipFlag = 1; 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; bool exitRequested = false;
GLFWwindow* window; GLFWwindow* window;
@ -65,15 +51,6 @@ mat4 viewingTransformation;
// Define a global scene graph root node // Define a global scene graph root node
SceneNode* rootNode; 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. * 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) { void renderNode(SceneNode* node) {
if (!node->model) return;
mat4 modelView; mat4 modelView;
identity(&modelView); identity(&modelView);
multiply(&modelView, &node->worldTransformation, &modelView); multiply(&modelView, &node->worldTransformation, &modelView);
@ -182,14 +130,14 @@ void renderNode(SceneNode* node) {
textureLocation = glGetUniformLocation(program, "textureSampler"); textureLocation = glGetUniformLocation(program, "textureSampler");
glUniform1i(textureLocation, 0); glUniform1i(textureLocation, 0);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[DAY]); glBindTexture(GL_TEXTURE_2D, node->model->texture);
textureLocation = glGetUniformLocation(program, "normalMap"); // textureLocation = glGetUniformLocation(program, "normalMap");
glUniform1i(textureLocation, 4); // glUniform1i(textureLocation, 4);
glActiveTexture(GL_TEXTURE4); // glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, textures[NORMAL]); // glBindTexture(GL_TEXTURE_2D, textures[NORMAL]);
draw_object(node->objectData); draw_object(node->model->objectData);
} }
void init(void) { void init(void) {
@ -260,30 +208,11 @@ void init(void) {
} }
// --------------- READ MODEL FILES // --------------- READ SCENE GRAPH
//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]);
}
setNodeRenderFunction(&renderNode); setNodeRenderFunction(&renderNode);
// Create the root scene node // read scene graph
rootNode = createSceneNode(); rootNode = loadSceneGraphFromFile("../scene-graph-example.scg");
// 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);
// ENABLE BACKFACE CULLING // ENABLE BACKFACE CULLING
glFrontFace(GL_CCW); 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, "lightColor"), 1.0f, 1.0f, 1.0f, 1.0f);
glUniform4f(glGetUniformLocation(program, "ambientLight"), 0.05f, 0.05f, 0.05f, 1.0f); glUniform4f(glGetUniformLocation(program, "ambientLight"), 0.05f, 0.05f, 0.05f, 1.0f);
// BIND TEXTURES vec4 lightPosition = {cos(stepi) * 5.0f, 5.0f, sin(stepi) * 5.0f, 1.0f};
GLuint textureLocation; multiplyAny(&lightPosition, &viewingTransformation, &lightPosition, 4, 4, 1);
textureLocation = glGetUniformLocation(program, "textureSampler");
glUniform1i(textureLocation, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[DAY]);
textureLocation = glGetUniformLocation(program, "normalMap"); glUniform3f(glGetUniformLocation(program, "lightPosition"), lightPosition.x, lightPosition.y, lightPosition.z);
glUniform1i(textureLocation, 4);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, textures[NORMAL]);
renderSceneNode(rootNode); renderSceneNode(rootNode);

View File

@ -94,7 +94,7 @@ ObjectData* readObjFiles(char** path, int numModels, int* count) {
* Takes a single object and reads it a certain number of times. * Takes a single object and reads it a certain number of times.
* Returns an array of objects. * Returns an array of objects.
*/ */
ObjectData* readSingleObjFile(char** path) { ObjectData* readSingleObjFile(char* path) {
ObjectData* objectData = (ObjectData*) malloc(sizeof(ObjectData)); ObjectData* objectData = (ObjectData*) malloc(sizeof(ObjectData));
if (!objectData) { if (!objectData) {
@ -103,7 +103,7 @@ ObjectData* readSingleObjFile(char** path) {
return NULL; return NULL;
} }
objectData->object = readObjFile(*path); objectData->object = readObjFile(path);
load_object(objectData); load_object(objectData);
return objectData; return objectData;

View File

@ -11,7 +11,7 @@ typedef struct {
extern void load_object(ObjectData* objectData); extern void load_object(ObjectData* objectData);
extern ObjectData* readObjFiles(char** path, int numModels, int* count); 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); extern void draw_object(ObjectData* objectData);
#endif #endif

View File

@ -3,6 +3,16 @@
#include "sceneGraph.h" #include "sceneGraph.h"
#include "objectHandler.h" #include "objectHandler.h"
#include <stdlib.h> #include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <GL/glew.h>
#include <stdbool.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../lib/stb_image.h"
#define SCG_LINE_BUFFER_SIZE 1024
#define SCG_KEYWORD_BUFFER_SIZE 32
void (*renderFunction)(SceneNode*); void (*renderFunction)(SceneNode*);
@ -10,13 +20,15 @@ void setNodeRenderFunction(void (*newRenderFunction)(SceneNode*)) {
renderFunction = newRenderFunction; renderFunction = newRenderFunction;
} }
SceneNode* createSceneNode() { SceneNode* createSceneNode(int id) {
SceneNode* node = (SceneNode*)malloc(sizeof(SceneNode)); SceneNode* node = (SceneNode*)malloc(sizeof(SceneNode));
identity(&node->transformation); identity(&node->transformation);
identity(&node->worldTransformation); identity(&node->worldTransformation);
node->id = id;
node->children = NULL; node->children = NULL;
node->numChildren = 0; node->numChildren = 0;
node->objectData = NULL; node->model = NULL;
node->name = NULL;
return node; return node;
} }
@ -34,7 +46,7 @@ void updateSceneNode(SceneNode* node, mat4* parentTransformation) {
} }
void renderSceneNode(SceneNode* node) { void renderSceneNode(SceneNode* node) {
if (node->objectData) { if (node->model) {
renderFunction(node); renderFunction(node);
} }
for (int i = 0; i < node->numChildren; i++) { for (int i = 0; i < node->numChildren; i++) {
@ -49,3 +61,258 @@ void freeSceneNode(SceneNode* node) {
free(node->children); free(node->children);
free(node); 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", &currentModel);
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(&currentNode->transformation, &currentNode->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(&currentNode->transformation, &currentNode->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(&currentNode->transformation, &currentNode->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(&currentNode->transformation, &currentNode->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(&currentNode->transformation, &currentNode->transformation, angle);
}
}
if (currentNode && !currentNodeHasParent) {
addChild(root, currentNode);
}
mat4 rootTransform;
identity(&rootTransform);
updateSceneNode(root, &rootTransform);
printSceneGraph(root, 0);
return root;
}

View File

@ -11,20 +11,27 @@
typedef struct SceneNode SceneNode; typedef struct SceneNode SceneNode;
typedef struct {
ObjectData* objectData;
GLuint texture;
} Model;
struct SceneNode { struct SceneNode {
int id;
mat4 transformation; // Local transformation matrix mat4 transformation; // Local transformation matrix
mat4 worldTransformation; // World transformation matrix mat4 worldTransformation; // World transformation matrix
SceneNode** children; // Array of pointers to child nodes SceneNode** children; // Array of pointers to child nodes
int numChildren; // Number of 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 SceneNode* createSceneNode();
extern void addChild(SceneNode* parent, SceneNode* child); extern void addChild(SceneNode* parent, SceneNode* child);
extern void updateSceneNode(SceneNode* node, mat4* parentTransformation); extern void updateSceneNode(SceneNode* node, mat4* parentTransformation);
extern void renderSceneNode(SceneNode* node); extern void renderSceneNode(SceneNode* node);
extern void freeSceneNode(SceneNode* node); extern void freeSceneNode(SceneNode* node);
extern SceneNode* loadSceneGraphFromFile(char* path);
#endif #endif

26
test-scene-graph.scg Normal file
View File

@ -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