365 lines
12 KiB
C
365 lines
12 KiB
C
// sceneGraph.c
|
|
|
|
#include "sceneGraph.h"
|
|
#include "objectHandler.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
|
|
|
|
#define KEYWORD_DEFINE_MODEL "model"
|
|
#define KEYWORD_DEFINE_NODE "obj"
|
|
|
|
#define KEYWORD_DEFINE_OBJ_FILE "file"
|
|
#define KEYWORD_DEFINE_TEXTURE_FILE "texture"
|
|
#define KEYWORD_DEFINE_SECONDARY_TEXTURE_FILE "texture2"
|
|
#define KEYWORD_DEFINE_NORMAL_MAP_FILE "normal"
|
|
#define KEYWORD_DEFINE_SHININESS "shininess"
|
|
|
|
#define KEYWORD_USE_MODEL "use"
|
|
#define KEYWORD_DEFINE_PARENT "parent"
|
|
#define KEYWORD_DEFINE_NAME "name"
|
|
|
|
#define KEYWORD_SCALE "scale"
|
|
#define KEYWORD_TRANSLATE "translate"
|
|
#define KEYWORD_ROTATE_X "rotateX"
|
|
#define KEYWORD_ROTATE_Y "rotateY"
|
|
#define KEYWORD_ROTATE_Z "rotateZ"
|
|
|
|
void (*renderFunction)(SceneNode*);
|
|
|
|
void setNodeRenderFunction(void (*newRenderFunction)(SceneNode*)) {
|
|
renderFunction = newRenderFunction;
|
|
}
|
|
|
|
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->model = NULL;
|
|
node->name = NULL;
|
|
return node;
|
|
}
|
|
|
|
void addChild(SceneNode* parent, SceneNode* child) {
|
|
parent->children = (SceneNode**)realloc(parent->children, sizeof(SceneNode*) * (parent->numChildren + 1));
|
|
parent->children[parent->numChildren] = child;
|
|
parent->numChildren++;
|
|
}
|
|
|
|
void updateSceneNode(SceneNode* node, mat4* parentTransformation) {
|
|
if (parentTransformation) {
|
|
multiply(&node->worldTransformation, parentTransformation, &node->transformation);
|
|
}
|
|
for (int i = 0; i < node->numChildren; i++) {
|
|
updateSceneNode(node->children[i], &node->worldTransformation);
|
|
}
|
|
}
|
|
|
|
void renderSceneNode(SceneNode* node) {
|
|
if (node->model) {
|
|
renderFunction(node);
|
|
}
|
|
for (int i = 0; i < node->numChildren; i++) {
|
|
renderSceneNode(node->children[i]);
|
|
}
|
|
}
|
|
|
|
void freeSceneNode(SceneNode* node) {
|
|
for (int i = 0; i < node->numChildren; i++) {
|
|
freeSceneNode(node->children[i]);
|
|
}
|
|
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, format, 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;
|
|
}
|
|
|
|
SceneNode* findNodeByName(char* name, SceneNode* root) {
|
|
if (root->name && strcmp(root->name, name) == 0) return root;
|
|
for (int i = 0; i < root->numChildren; i++) {
|
|
SceneNode* node = findNodeByName(name, 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, KEYWORD_DEFINE_MODEL) == 0) {
|
|
int modelId = 0;
|
|
sscanf(buf, KEYWORD_DEFINE_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, KEYWORD_DEFINE_MODEL) == 0) {
|
|
sscanf(buf, KEYWORD_DEFINE_MODEL " %d", ¤tModel);
|
|
models[currentModel].objectData = NULL;
|
|
models[currentModel].texture = -1;
|
|
models[currentModel].secondaryTexture = -1;
|
|
models[currentModel].normalMap = -1;
|
|
models[currentModel].shininess = 10.0f;
|
|
continue;
|
|
}
|
|
if (strcmp(keyword, KEYWORD_DEFINE_OBJ_FILE) == 0) {
|
|
sscanf(buf, KEYWORD_DEFINE_OBJ_FILE " %s", filepathBuffer);
|
|
models[currentModel].objectData = readSingleObjFile(filepathBuffer);
|
|
continue;
|
|
}
|
|
if (strcmp(keyword, KEYWORD_DEFINE_TEXTURE_FILE) == 0) {
|
|
sscanf(buf, KEYWORD_DEFINE_TEXTURE_FILE " %s", filepathBuffer);
|
|
loadTexture(filepathBuffer, &models[currentModel].texture);
|
|
continue;
|
|
}
|
|
if (strcmp(keyword, KEYWORD_DEFINE_SECONDARY_TEXTURE_FILE) == 0) {
|
|
sscanf(buf, KEYWORD_DEFINE_SECONDARY_TEXTURE_FILE " %s", filepathBuffer);
|
|
loadTexture(filepathBuffer, &models[currentModel].secondaryTexture);
|
|
continue;
|
|
}
|
|
if (strcmp(keyword, KEYWORD_DEFINE_NORMAL_MAP_FILE) == 0) {
|
|
sscanf(buf, KEYWORD_DEFINE_NORMAL_MAP_FILE " %s", filepathBuffer);
|
|
loadTexture(filepathBuffer, &models[currentModel].normalMap);
|
|
continue;
|
|
}
|
|
if (strcmp(keyword, KEYWORD_DEFINE_SHININESS) == 0) {
|
|
sscanf(buf, KEYWORD_DEFINE_SHININESS " %f", &models[currentModel].shininess);
|
|
continue;
|
|
}
|
|
|
|
|
|
if (strcmp(keyword, KEYWORD_DEFINE_NODE) == 0) {
|
|
|
|
if (currentNode && !currentNodeHasParent) {
|
|
addChild(root, currentNode);
|
|
}
|
|
|
|
currentNode = NULL;
|
|
currentNodeHasParent = false;
|
|
|
|
int nodeId = 0;
|
|
sscanf(buf, KEYWORD_DEFINE_NODE " %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, KEYWORD_DEFINE_NAME) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected, can't assign name - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
sscanf(buf, KEYWORD_DEFINE_NAME " %s", filepathBuffer);
|
|
char* name = (char*)malloc(strlen(filepathBuffer) * sizeof(char) + 1);
|
|
strcpy(name, filepathBuffer);
|
|
currentNode->name = name;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(keyword, KEYWORD_DEFINE_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, KEYWORD_DEFINE_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, KEYWORD_USE_MODEL) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected to assign model to - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
|
|
int usedModel = 0;
|
|
sscanf(buf, KEYWORD_USE_MODEL " %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, KEYWORD_TRANSLATE) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected to assign position to - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
vec3 translation;
|
|
sscanf(buf, KEYWORD_TRANSLATE " %f %f %f", &translation.x, &translation.y, &translation.z);
|
|
translate(¤tNode->transformation, ¤tNode->transformation, &translation);
|
|
}
|
|
|
|
if (strcmp(keyword, KEYWORD_SCALE) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected to assign scale to - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
vec3 translation;
|
|
sscanf(buf, KEYWORD_SCALE " %f %f %f", &translation.x, &translation.y, &translation.z);
|
|
scale(¤tNode->transformation, ¤tNode->transformation, &translation);
|
|
}
|
|
|
|
if (strcmp(keyword, KEYWORD_ROTATE_X) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected to assign rotationX to - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
GLfloat angle;
|
|
sscanf(buf, KEYWORD_ROTATE_X " %f", &angle);
|
|
rotateX(¤tNode->transformation, ¤tNode->transformation, angle);
|
|
}
|
|
if (strcmp(keyword, KEYWORD_ROTATE_Y) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected to assign rotationY to - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
GLfloat angle;
|
|
sscanf(buf, KEYWORD_ROTATE_Y " %f", &angle);
|
|
rotateY(¤tNode->transformation, ¤tNode->transformation, angle);
|
|
}
|
|
if (strcmp(keyword, KEYWORD_ROTATE_Z) == 0) {
|
|
if (!currentNode) {
|
|
fprintf(stderr, "no node selected to assign rotationZ to - line %d\n", currentLine);
|
|
return NULL;
|
|
}
|
|
GLfloat angle;
|
|
sscanf(buf, KEYWORD_ROTATE_Z " %f", &angle);
|
|
rotateZ(¤tNode->transformation, ¤tNode->transformation, angle);
|
|
}
|
|
}
|
|
|
|
if (currentNode && !currentNodeHasParent) {
|
|
addChild(root, currentNode);
|
|
}
|
|
|
|
updateSceneNode(root, NULL);
|
|
|
|
printSceneGraph(root, 0);
|
|
|
|
return root;
|
|
} |