diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/cg1_purple.iml b/.idea/cg1_purple.iml new file mode 100644 index 0000000..4c94235 --- /dev/null +++ b/.idea/cg1_purple.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..855412d --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,103 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9fc3e1c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/fragmentShader.glsl b/src/fragmentShader.glsl index 79e05d1..92c8570 100644 --- a/src/fragmentShader.glsl +++ b/src/fragmentShader.glsl @@ -5,7 +5,7 @@ in vec3 fragmentPosition; in vec2 textureCoordinate; flat in mat3 TBN; - +in vec3 skyboxCoord; uniform float shininess; @@ -18,41 +18,49 @@ uniform sampler2D night; uniform sampler2D clouds; uniform sampler2D ocean; uniform sampler2D normalMap; +uniform samplerCube skyboxSampler; +uniform bool isSkybox; float emissionStrength = 0.0; void main() { - vec4 color = vec4(texture(day, textureCoordinate).rgb, 1.0); + if (isSkybox) { + // for skybox-texture + gl_FragColor = texture(skyboxSampler, skyboxCoord); + } else { + // for regular-textures + vec4 color = vec4(texture(day, textureCoordinate).rgb, 1.0); - float nightBrightness = texture(night, textureCoordinate).r; - vec4 nightColor = vec4(nightBrightness, nightBrightness * 0.7, nightBrightness * 0.5, 1.0); - float shininessMultiplier = texture(ocean, textureCoordinate).r; + float nightBrightness = texture(night, textureCoordinate).r; + vec4 nightColor = vec4(nightBrightness, nightBrightness * 0.7, nightBrightness * 0.5, 1.0); + float shininessMultiplier = texture(ocean, textureCoordinate).r; - vec4 cloudColor = texture(clouds, textureCoordinate).rgba; + vec4 cloudColor = texture(clouds, textureCoordinate).rgba; - vec3 norm = normalize(normal); + vec3 norm = normalize(normal); - vec3 lightDir = normalize(lightPosition - fragmentPosition); - vec3 eyeDir = (-normalize(fragmentPosition)); + vec3 lightDir = normalize(lightPosition - fragmentPosition); + vec3 eyeDir = (-normalize(fragmentPosition)); - float diff = max(dot(norm, lightDir), 0.0); + float diff = max(dot(norm, lightDir), 0.0); - vec3 halfway = (lightDir + eyeDir) / length(lightDir + eyeDir); - float specular = pow(max(dot(halfway, norm), 0.0), shininess) * shininessMultiplier; + vec3 halfway = (lightDir + eyeDir) / length(lightDir + eyeDir); + float specular = pow(max(dot(halfway, norm), 0.0), shininess) * shininessMultiplier; - gl_FragColor = - // EMISSION - color * emissionStrength + + gl_FragColor = + // EMISSION + color * emissionStrength + - // // AMBIENT - ambientLight * color + + // // AMBIENT + ambientLight * color + - // DIFFUSION - mix(nightColor * (vec4(1,1,1,1) - cloudColor) + (cloudColor * ambientLight), color + cloudColor, diff) + + // DIFFUSION + mix(nightColor * (vec4(1,1,1,1) - cloudColor) + (cloudColor * ambientLight), color + cloudColor, diff) + - // SPECULAR - specular * lightColor * color; + // SPECULAR + specular * lightColor * color; + } } \ No newline at end of file diff --git a/src/main.c b/src/main.c index f877312..d0a2130 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,4 @@ #include - #include #include @@ -9,6 +8,7 @@ #include "matrixMath.h" #include "transformation.h" #include "wavefrontobj.h" +#include "sceneGraph.h" #define STB_IMAGE_IMPLEMENTATION #include "../lib/stb_image.h" @@ -61,6 +61,13 @@ vec3 cameraPosition = {0.0f, 3.0f, 5.5f}; char* defaultModel = "../obj/monkey.obj"; char* model; +// Define a global scene graph root node +SceneNode* rootNode; + +// skybox date +GLuint skyboxVAO, skyboxVBO; +GLuint skyboxTexture; + // input handler for camera movement void handleInputs(double deltaTime) { if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { @@ -92,6 +99,12 @@ void keyboardHandler(GLFWwindow* window, int key, int scancode, int action, int } } +// Define the function to render a node (e.g., rendering an object) +void renderNode(SceneNode* node) { + glUniformMatrix4fv(glGetUniformLocation(program, "modelView"), 1, GL_FALSE, (GLfloat*)&node->worldTransformation); + glDrawArrays(GL_TRIANGLES, 0, numFaces * 3); +} + void loadTexture(char* textureFile, GLuint* texture) { int width, height, nrChannels; unsigned char* image = stbi_load(textureFile, &width, &height, &nrChannels, 0); @@ -120,6 +133,115 @@ void loadTexture(char* textureFile, GLuint* texture) { stbi_image_free(image); } +void loadCubemap(const char* faces[6], GLuint* textureID) { + glGenTextures(1, textureID); + glBindTexture(GL_TEXTURE_CUBE_MAP, *textureID); + + int width, height, nrChannels; + for (unsigned int i = 0; i < 6; i++) { + unsigned char* data = stbi_load(faces[i], &width, &height, &nrChannels, 0); + if (data) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, + 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + stbi_image_free(data); + } else { + printf("Cubemap texture failed to load at path: %s\n", faces[i]); + stbi_image_free(data); + } + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +} + +void initSkybox() { + float skyboxVertices[] = { + // positions + -1.0f, 1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + + -1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + glGenVertexArrays(1, &skyboxVAO); + glGenBuffers(1, &skyboxVBO); + glBindVertexArray(skyboxVAO); + glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glBindVertexArray(0); + + const char* faces[6] = { + "../texture/skybox/right.jpg", + "../texture/skybox/left.jpg", + "../texture/skybox/top.jpg", + "../texture/skybox/bottom.jpg", + "../texture/skybox/front.jpg", + "../texture/skybox/back.jpg" + }; + loadCubemap(faces, &skyboxTexture); +} + +void renderSkybox(mat4* viewMatrix, mat4* projectionMatrix) { + glDepthFunc(GL_LEQUAL); + glUseProgram(program); + + mat4 view = *viewMatrix; + view.m30 = 0.0f; + view.m31 = 0.0f; + view.m32 = 0.0f; + glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, (GLfloat*)&view); + glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, (GLfloat*)projectionMatrix); + + glBindVertexArray(skyboxVAO); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + + glDepthFunc(GL_LESS); +} + void init(void) { // create and compile vertex shader const GLchar *vertexTextConst = vertexShader_glsl; @@ -262,6 +384,17 @@ void init(void) { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); + // Create the root scene node + rootNode = createSceneNode(); + + // Create a child node (e.g., for the model) + SceneNode* modelNode = createSceneNode(); + modelNode->render = renderNode; + addChild(rootNode, modelNode); + + // Set transformations for the modelNode (example) + rotateY(&modelNode->transformation, &modelNode->transformation, pi / 4); + // ENABLE BACKFACE CULLING glFrontFace(GL_CCW); @@ -271,6 +404,9 @@ void init(void) { glEnable(GL_DEPTH_TEST); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + + // initialize skybox + initSkybox(); } void updateStats() { @@ -297,9 +433,15 @@ void draw(void) { handleInputs(deltaTime); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(program); + glUseProgram(program); glBindVertexArray(vao); + // Update and render the scene graph + mat4 identityMatrix; + identity(&identityMatrix); + updateSceneNode(rootNode, &identityMatrix); + renderSceneNode(rootNode); + // step for rotations // counts up to 1.0 and then resets back to 0.0 forever step += deltaTime / 15; @@ -408,6 +550,9 @@ void draw(void) { // draw!!1 glDrawArrays(GL_TRIANGLES, 0, numFaces * 3); + + // draw skybox + renderSkybox(&viewingTransformation, &projectionTransformation); } // change viewport size and adjust aspect ratio when changing window size @@ -457,11 +602,10 @@ int main(int argc, char **argv) { // exit when window should close or exit is requested (ESC) while (!glfwWindowShouldClose(window) && !exitRequested) { draw(); - glfwSwapBuffers(window); glfwPollEvents(); } - + freeSceneNode(rootNode); glfwTerminate(); return 0; diff --git a/src/sceneGraph.c b/src/sceneGraph.c new file mode 100644 index 0000000..804cf5f --- /dev/null +++ b/src/sceneGraph.c @@ -0,0 +1,44 @@ +// sceneGraph.c + +#include "sceneGraph.h" +#include + +SceneNode* createSceneNode() { + SceneNode* node = (SceneNode*)malloc(sizeof(SceneNode)); + identity(&node->transformation); + identity(&node->worldTransformation); + node->render = NULL; + node->children = NULL; + node->numChildren = 0; + 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) { + 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->render) { + node->render(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); +} \ No newline at end of file diff --git a/src/sceneGraph.h b/src/sceneGraph.h new file mode 100644 index 0000000..0fe5d57 --- /dev/null +++ b/src/sceneGraph.h @@ -0,0 +1,23 @@ +// sceneGraph.h + +#ifndef SCENE_GRAPH_H +#define SCENE_GRAPH_H + +#include +#include "matrixMath.h" + +typedef struct SceneNode { + mat4 transformation; // Local transformation matrix + mat4 worldTransformation; // World transformation matrix + void (*render)(struct SceneNode*); // Function pointer to render this node + struct SceneNode** children; // Array of pointers to child nodes + int numChildren; // Number of child nodes +} SceneNode; + +SceneNode* createSceneNode(); +void addChild(SceneNode* parent, SceneNode* child); +void updateSceneNode(SceneNode* node, mat4* parentTransformation); +void renderSceneNode(SceneNode* node); +void freeSceneNode(SceneNode* node); + +#endif \ No newline at end of file diff --git a/src/vertexShader.glsl b/src/vertexShader.glsl index 551573f..d94a0ff 100644 --- a/src/vertexShader.glsl +++ b/src/vertexShader.glsl @@ -7,30 +7,45 @@ layout (location = 3) in vec3 aTangent; uniform mat4 modelView; uniform mat3 normalModelView; uniform mat4 projection; +uniform bool isSkybox; out vec3 normal; out vec3 fragmentPosition; out vec2 textureCoordinate; flat out mat3 TBN; +out vec3 skyboxCoord; + void main() { - textureCoordinate = aTextureCoordinate; + if (isSkybox) { + // for skybox + skyboxCoord = aPosition; - vec3 tangent = normalize(normalModelView * aTangent); - normal = normalize(normalModelView * aNormal); + vec4 modelViewPos = modelView * vec4(aPosition, 1.0); - vec3 bitangent = normalize(cross(normal, tangent)); + gl_position = projection * vec4(aPosition, 1.0); - TBN = transpose(mat3( - tangent, - bitangent, - normal - )); + fragmentPosition = vec3(modelViewPos); + } else { + // for regular objects + textureCoordinate = aTextureCoordinate; + + vec3 tangent = normalize(normalModelView * aTangent); + normal = normalize(normalModelView * aNormal); + + vec3 bitangent = normalize(cross(normal, tangent)); + + TBN = transpose(mat3( + tangent, + bitangent, + normal + )); - vec4 modelViewPos = modelView * vec4(aPosition, 1.0); + vec4 modelViewPos = modelView * vec4(aPosition, 1.0); - gl_Position = projection * modelViewPos; + gl_Position = projection * modelViewPos; - fragmentPosition = vec3(modelViewPos); + fragmentPosition = vec3(modelViewPos); + } } \ No newline at end of file diff --git a/texture/Cubemaps/Test_map.png b/texture/Cubemaps/Test_map.png new file mode 100644 index 0000000..0d64445 Binary files /dev/null and b/texture/Cubemaps/Test_map.png differ