]> git.lizzy.rs Git - shadowclad.git/commitdiff
Merge branch 'master' into crossbuild
authoroutfrost <kotlet.bahn@gmail.com>
Thu, 6 Aug 2020 00:41:16 +0000 (02:41 +0200)
committeroutfrost <kotlet.bahn@gmail.com>
Thu, 6 Aug 2020 00:41:16 +0000 (02:41 +0200)
32 files changed:
Makefile
src/engine/_prelude.h [new file with mode: 0644]
src/engine/asset.c
src/engine/asset.h
src/engine/engine.c [new file with mode: 0644]
src/engine/engine.h [new file with mode: 0644]
src/engine/geometry.c
src/engine/geometry.h
src/engine/input.c [new file with mode: 0644]
src/engine/input.h [new file with mode: 0644]
src/engine/logger.c
src/engine/logger.h
src/engine/performance.c
src/engine/performance.h
src/engine/render.c
src/engine/render.h
src/engine/scene.h
src/engine/string.c [new file with mode: 0644]
src/engine/string.h [new file with mode: 0644]
src/engine/tga.c
src/engine/tga.h
src/engine/ui.c
src/engine/ui.h
src/game/game.c [new file with mode: 0644]
src/game/game.h [new file with mode: 0644]
src/game/input.c [new file with mode: 0644]
src/game/input.h [new file with mode: 0644]
src/game/level.c
src/game/level.h
src/game/player.c
src/game/player.h
src/main.c

index 106065543eb10c4245027332f0b8c04bb5efa699..6939f7465b99ad28b35cc00dbd0ffa008a1ea1d4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,9 +7,11 @@ BUILDDIR ?= target/$(PLATFORM)
 SRCDIR ?= src
 
 CPPFLAGS ::= -iquotesrc/ $(CPPFLAGS)
-CFLAGS ::= -g -std=c99 -Wall -Wextra -Wpedantic -Werror $(CFLAGS)
+CFLAGS ::= -g -std=c99 -Wall -Wextra -Wpedantic -Werror \
+           -Wno-error=unused-function -Wno-error=unused-parameter $(CFLAGS)
 LDFLAGS ::= $(LDFLAGS)
-LDLIBS ::= -L/usr/x86_64-w64-mingw32/lib -lopengl32 -lglew32 -lfreeglut -lassimp $(LDLIBS)
+#LDLIBS ::= -L/usr/x86_64-w64-mingw32/lib -lopengl32 -lglew32 -lfreeglut -lassimp $(LDLIBS)
+LDLIBS ::= -lm -lopengl32 -lglew32 -lglfw -lassimp $(LDLIBS)
 
 # ######
 # Paths
@@ -17,13 +19,18 @@ LDLIBS ::= -L/usr/x86_64-w64-mingw32/lib -lopengl32 -lglew32 -lfreeglut -lassimp
 
 sources ::= main.c \
             engine/asset.c \
+            engine/engine.c \
             engine/geometry.c \
+            engine/input.c \
             engine/logger.c \
             engine/performance.c \
             engine/render.c \
             engine/scene.c \
+            engine/string.c \
             engine/tga.c \
             engine/ui.c \
+            game/game.c \
+            game/input.c \
             game/level.c \
             game/player.c
 
diff --git a/src/engine/_prelude.h b/src/engine/_prelude.h
new file mode 100644 (file)
index 0000000..a97c9ba
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef ENGINE_PRELUDE_H_
+#define ENGINE_PRELUDE_H_
+
+#ifdef __GNUC__
+
+#      define UNUSED __attribute__((unused))
+
+#else // __GNUC__
+
+#      define UNUSED
+
+#endif // __GNUC__
+
+#endif // ENGINE_PRELUDE_H_
index 8bb19e50a686099ad95470b35bd13fcda46aa4f2..11239acc56be4374299228f7be06d93d7e84cabc 100644 (file)
 #include "asset.h"
 
+#include <math.h>
 #include <stdlib.h>
 #include <assimp/cimport.h>
+//#include <assimp/metadata.h>
 #include <assimp/postprocess.h>
 #include <assimp/scene.h>
 
 #include "logger.h"
+#include "string.h"
 #include "tga.h"
 
+#define IMPORT_DEBUG_ 0
+
+static const float smoothingThresholdAngle = TAU / 14.0f;
+
 static const struct aiScene* importScene(const char* path);
-static Vector3D convertAiVector3D(struct aiVector3D vect);
+static Vector triangleNormal(Vector v1, Vector v2, Vector v3);
+static Vector convertAiVector3D(struct aiVector3D vect);
 static const char* replaceFileExtension(const struct aiString path, const char* ext);
 
 
 
+#if IMPORT_DEBUG_
+static void printMetadata(const struct aiMetadata* meta) {
+       if (meta) {
+               for (size_t i = 0; i < meta->mNumProperties; ++i) {
+                       String key = stringFromAiString(meta->mKeys[i]);
+                       const struct aiMetadataEntry value = meta->mValues[i];
+                       switch (value.mType) {
+                               case AI_BOOL:
+                                       logDebug("\"%s\": (bool) %d", key.cstr, *((int*) value.mData));
+                                       break;
+                               case AI_INT32:
+                                       logDebug("\"%s\": (int32) %d", key.cstr, *((int32_t*) value.mData));
+                                       break;
+                               case AI_UINT64:
+                                       logDebug("\"%s\": (uint64) %llu", key.cstr, *((uint64_t*) value.mData));
+                                       break;
+                               case AI_FLOAT:
+                                       logDebug("\"%s\": (float) %f", key.cstr, *((float*) value.mData));
+                                       break;
+                               case AI_DOUBLE:
+                                       logDebug("\"%s\": (double) %f", key.cstr, *((double*) value.mData));
+                                       break;
+                               case AI_AISTRING: {
+                                       String str = stringFromAiString(*((struct aiString*) value.mData));
+                                       logDebug("\"%s\": (string) %s", key.cstr, str.cstr);
+                                       dropString(str);
+                                       break; }
+                               case AI_AIVECTOR3D: {
+                                       struct aiVector3D vec = *((struct aiVector3D*) value.mData);
+                                       logDebug("\"%s\": (vector3d) { %f, %f, %f }", key.cstr, vec.x, vec.y, vec.z);
+                                       break; }
+                               case AI_META_MAX:
+                               default:
+                                       logDebug("\"%s\": (unrecognized type)", key.cstr);
+                                       break;
+                       }
+                       dropString(key);
+               }
+       }
+}
+
+void printAiNodeMetadata(const struct aiNode* node) {
+       if (!node) {
+               return;
+       }
+
+       String name = stringFromAiString(node->mName);
+       logDebug("Metadata from node \"%s\": %p", name.cstr, node->mMetaData);
+       printMetadata(node->mMetaData);
+
+       for (size_t i = 0; i < node->mNumChildren; ++i) {
+               printAiNodeMetadata(node->mChildren[i]);
+       }
+
+       dropString(name);
+}
+#endif // IMPORT_DEBUG_
+
 const Solid* importSolid(const char* path) {
        const struct aiScene* scene = importScene(path);
        if (scene == NULL) {
                logError("Failed to import solid from %s", path);
                return NULL;
        }
-       
+
+#if IMPORT_DEBUG_
+       const struct aiMetadata* meta = scene->mMetaData;
+       logDebug("Metadata from %s: %p", path, meta);
+       printMetadata(meta);
+       printAiNodeMetadata(scene->mRootNode);
+#endif // IMPORT_DEBUG_
+
        const unsigned int numMeshes = scene->mNumMeshes;
        const unsigned int numMaterials = scene->mNumMaterials;
 
        // TODO Consider assets with some arrays empty, and prevent zero mallocs
-       
+
        Solid* solid = malloc(sizeof(Solid));
        solid->numMeshes = numMeshes;
        solid->meshes = malloc(numMeshes * sizeof(Mesh));
        solid->numMaterials = numMaterials;
        solid->materials = malloc(numMaterials * sizeof(Material));
-       
+
        for (unsigned int meshIndex = 0; meshIndex < numMeshes; ++meshIndex) {
                const struct aiMesh* aiMesh = scene->mMeshes[meshIndex];
                const unsigned int numVertices = aiMesh->mNumVertices;
                const unsigned int numFaces = aiMesh->mNumFaces;
-               
+
                Mesh mesh = { .numVertices = numVertices,
-                             .vertices = malloc(numVertices * sizeof(Vector3D)),
+                             .vertices = malloc(numVertices * sizeof(Vector)),
                              .normals = NULL,
                              .textureCoords = NULL,
                              .numFaces = numFaces,
                              .faces = malloc(numFaces * sizeof(Face)),
                              .materialIndex = aiMesh->mMaterialIndex };
-               
+
                for (unsigned int vertIndex = 0; vertIndex < numVertices; ++vertIndex) {
                        mesh.vertices[vertIndex] = convertAiVector3D(
                                        aiMesh->mVertices[vertIndex]);
                }
-               
+
                if (aiMesh->mNormals != NULL) {
-                       mesh.normals = malloc(numVertices * sizeof(Vector3D));
+                       mesh.normals = malloc(numVertices * sizeof(Vector));
                        for (unsigned int normIndex = 0; normIndex < numVertices; ++normIndex) {
                                mesh.normals[normIndex] = convertAiVector3D(
                                                aiMesh->mNormals[normIndex]);
                        }
                }
-               
-               mesh.textureCoords = malloc(numVertices * sizeof(Vector3D));
+
+               mesh.textureCoords = malloc(numVertices * sizeof(Vector));
                for (unsigned int texcIndex = 0; texcIndex < numVertices; ++texcIndex) {
                        mesh.textureCoords[texcIndex] = convertAiVector3D(
                                        aiMesh->mTextureCoords[0][texcIndex]);
                }
-               
+
                for (unsigned int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
                        const struct aiFace aiFace = aiMesh->mFaces[faceIndex];
                        const unsigned int numIndices = aiFace.mNumIndices;
-                       
+
                        Face face = { .numIndices = numIndices,
-                                     .indices = malloc(numIndices * sizeof(size_t)) };
-                       
+                                     .indices = malloc(numIndices * sizeof(size_t)),
+                                     .normals = malloc(numIndices * sizeof(Vector)) };
+
                        for (unsigned int i = 0; i < numIndices; ++i) {
                                face.indices[i] = aiFace.mIndices[i];
                        }
-                       
+
+                       if (numIndices == 3) {
+                               Vector normal = triangleNormal(mesh.vertices[face.indices[0]],
+                                                              mesh.vertices[face.indices[1]],
+                                                              mesh.vertices[face.indices[2]]);
+                               for (size_t i = 0; i < numIndices; ++i) {
+                                       face.normals[i] = normal;
+                               }
+                       }
+                       else {
+                               if (mesh.normals) {
+                                       for (size_t i = 0; i < numIndices; ++i) {
+                                               face.normals[i] = mesh.normals[face.indices[i]];
+                                       }
+                               }
+                               else {
+                                       free(face.normals);
+                                       face.normals = NULL;
+                               }
+                       }
+
                        mesh.faces[faceIndex] = face;
                }
-               
+
+               float smoothingThreshold = cosf(smoothingThresholdAngle);
+               Face* smoothedFaces = malloc(mesh.numFaces * sizeof(Face));
+               for (size_t faceIndex = 0; faceIndex < mesh.numFaces; ++faceIndex) {
+                       Face face = mesh.faces[faceIndex];
+
+                       if (face.normals) {
+                               face.normals = memcpy(malloc(face.numIndices * sizeof(Vector)),
+                                                     face.normals,
+                                                     face.numIndices * sizeof(Vector));
+
+                               for (size_t indexIndex = 0; indexIndex < face.numIndices; ++indexIndex) {
+                                       Vector smoothedNormal = face.normals[indexIndex];
+
+                                       for (size_t i = 0; i < mesh.numFaces; ++i) {
+                                               if (i == faceIndex || !mesh.faces[i].normals) {
+                                                       continue;
+                                               }
+
+                                               for (size_t k = 0; k < mesh.faces[i].numIndices; ++k) {
+                                                       if (mesh.faces[i].indices[k] == face.indices[indexIndex]
+                                                           && dotProduct(face.normals[indexIndex],
+                                                                         mesh.faces[i].normals[k]) >= smoothingThreshold) {
+                                                               smoothedNormal = addVectors(smoothedNormal, mesh.faces[i].normals[k]);
+                                                       }
+                                               }
+                                       }
+
+                                       face.normals[indexIndex] = normalized(smoothedNormal);
+                               }
+                       }
+                       smoothedFaces[faceIndex] = face;
+               }
+               // TODO Actually clean up the stuff inside
+               free(mesh.faces);
+               mesh.faces = smoothedFaces;
+
                solid->meshes[meshIndex] = mesh;
        }
-       
+
        GLuint* textureIds = malloc(numMaterials * sizeof(GLuint));
        glGenTextures(numMaterials, textureIds);
-       
+
        for (unsigned int matIndex = 0; matIndex < numMaterials; ++matIndex) {
                Material material = { .textureId = textureIds[matIndex] };
-               
+
+#if IMPORT_DEBUG_
+               const struct aiMaterialProperty* prop;
+               aiGetMaterialProperty(scene->mMaterials[matIndex],
+                                     AI_MATKEY_SHADING_MODEL,
+                                     &prop);
+
+               String key = stringFromAiString(prop->mKey);
+
+               logDebug("Material property \"%s\": Shading model: (length %u) %d",
+                        key.cstr,
+                        prop->mDataLength,
+                        *((int32_t*) prop->mData));
+
+               dropString(key);
+#endif // IMPORT_DEBUG_
+
                glBindTexture(GL_TEXTURE_2D, material.textureId);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-               
+
                struct aiString originalTexturePath;
                if (aiGetMaterialTexture(scene->mMaterials[matIndex],
                                         aiTextureType_DIFFUSE,
@@ -101,7 +247,7 @@ const Solid* importSolid(const char* path) {
                        char* texturePath = malloc(strlen("assets/") + textureFilenameLength + 1);
                        strcpy(texturePath, "assets/");
                        strncat(texturePath, textureFilename, textureFilenameLength);
-                       
+
                        TgaImage* textureImage = readTga(texturePath);
                        if (textureImage == NULL) {
                                logError("Importing solid from %s: Cannot read texture file %s", path, texturePath);
@@ -120,17 +266,19 @@ const Solid* importSolid(const char* path) {
                                free(textureImage);
                        }
                }
-               
+
                solid->materials[matIndex] = material;
        }
        glBindTexture(GL_TEXTURE_2D, 0);
-       
+
        aiReleaseImport(scene);
        return solid;
 }
 
 static const struct aiScene* importScene(const char* path) {
-       const struct aiScene* scene = aiImportFile(path, aiProcess_PreTransformVertices);
+       const struct aiScene* scene = aiImportFile(path, aiProcess_JoinIdenticalVertices
+                                                        | aiProcess_PreTransformVertices
+                                                        | aiProcess_ValidateDataStructure);
        if (scene != NULL && scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) {
                logError("Incomplete scene imported from %s", path);
                aiReleaseImport(scene);
@@ -139,10 +287,14 @@ static const struct aiScene* importScene(const char* path) {
        return scene;
 }
 
-static Vector3D convertAiVector3D(struct aiVector3D vect) {
-       return (Vector3D) { .x = vect.x,
-                           .y = vect.y,
-                           .z = vect.z };
+static Vector triangleNormal(Vector v1, Vector v2, Vector v3) {
+       return normalized(crossProduct(subtractVectors(v2, v1), subtractVectors(v3, v1)));
+}
+
+static Vector convertAiVector3D(struct aiVector3D vect) {
+       return (Vector) { .x = vect.x,
+                         .y = vect.y,
+                         .z = vect.z };
 }
 
 /**
@@ -152,18 +304,18 @@ static Vector3D convertAiVector3D(struct aiVector3D vect) {
  */
 static const char* replaceFileExtension(const struct aiString path, const char* ext) {
                size_t lengthToCopy = path.length;
-               
+
                char* lastDotSubstr = strrchr(path.data, '.');
                if (lastDotSubstr != NULL) {
                        if (strpbrk(lastDotSubstr, "\\/") == NULL) {
                                lengthToCopy = lastDotSubstr - path.data;
                        }
                }
-               
+
                size_t extLength = strlen(ext) + 1;
                char* newPath = malloc(lengthToCopy + extLength);
                strncpy(newPath, path.data, lengthToCopy);
                strncpy(newPath + lengthToCopy, ext, extLength);
-               
+
                return newPath;
 }
index 9354c47c7615faa952baf8112b03ae98da855985..dea64c2731c5417e7867af325fb842bd9bb72c7e 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef ASSET_H_
-#define ASSET_H_
+#ifndef ENGINE_ASSET_H_
+#define ENGINE_ASSET_H_
 
 #include <stddef.h>
 #include <GL/gl.h>
@@ -20,9 +20,9 @@ struct Solid {
 
 struct Mesh {
        size_t numVertices;
-       Vector3D* vertices;
-       Vector3D* normals;
-       Vector3D* textureCoords;
+       Vector* vertices;
+       Vector* normals;
+       Vector* textureCoords;
        size_t numFaces;
        Face* faces;
        size_t materialIndex;
@@ -31,6 +31,7 @@ struct Mesh {
 struct Face {
        size_t numIndices;
        size_t* indices;
+       Vector* normals;
 };
 
 struct Material {
@@ -39,4 +40,4 @@ struct Material {
 
 const Solid* importSolid(const char* path);
 
-#endif // ASSET_H_
+#endif // ENGINE_ASSET_H_
diff --git a/src/engine/engine.c b/src/engine/engine.c
new file mode 100644 (file)
index 0000000..5ebb975
--- /dev/null
@@ -0,0 +1,117 @@
+#include "engine.h"
+
+#include <stdlib.h>
+#include <assimp/version.h>
+//#include <GL/glxew.h>
+#include <GLFW/glfw3.h>
+
+#include "_prelude.h"
+#include "input.h"
+#include "logger.h"
+#include "performance.h"
+#include "render.h"
+#include "ui.h"
+
+// static const int EXIT_OK = 0;
+static const int EXIT_LIB_FAIL = 1;
+static const int EXIT_CTX_FAIL = 2;
+
+static GLFWwindow* window;
+
+static void onGlfwError(int error, const char* description);
+
+
+
+void init(EngineConfig config) {
+       if (window) {
+               logError("init called more than once");
+               return;
+       }
+
+       logInfo("Assimp %u.%u", aiGetVersionMajor(), aiGetVersionMinor());
+       logInfo("GLEW %s", (const char*) glewGetString(GLEW_VERSION));
+       logInfo("GLFW %s", glfwGetVersionString());
+
+       glfwSetErrorCallback(onGlfwError);
+
+       if (!glfwInit()) {
+               logError("GLFW init failed");
+               exit(EXIT_LIB_FAIL);
+       }
+       // glutInitContextVersion(4,5); TODO establish correct context
+       // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+       // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
+
+       window = glfwCreateWindow(config.windowWidth,
+                                 config.windowHeight,
+                                 config.windowTitle.cstr,
+                                 NULL,
+                                 NULL);
+
+       if (!window) {
+               logError("Window or context creation failed");
+               glfwTerminate();
+               exit(EXIT_CTX_FAIL);
+       }
+
+       glfwMakeContextCurrent(window);
+
+       logInfo("OpenGL %s", (const char*) glGetString(GL_VERSION));
+//     logInfo("GLSL %s", (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
+       logInfo("Renderer: %s", (const char*) glGetString(GL_RENDERER));
+/*
+       GLenum glewInitStatus = glewInit();
+       if (glewInitStatus != GLEW_OK) {
+               logError("GLEW init failed: %s", (const char*) glewGetErrorString(glewInitStatus));
+               exit(EXIT_LIB_FAIL);
+       }
+*/
+       logInfo("Setting swap interval: %d", config.swapInterval);
+       glfwSwapInterval(config.swapInterval);
+
+       int width, height;
+       glfwGetFramebufferSize(window, &width, &height);
+       resizeStage(window, width, height);
+
+       glfwSetFramebufferSizeCallback(window, resizeStage);
+       glfwSetKeyCallback(window, onKeyboardEvent);
+
+       initRender();
+       //initPerformanceMetering();
+}
+
+void run(void (*updateFn) (float)) {
+       if (!updateFn) {
+               logError("No update function provided");
+               return;
+       }
+
+       float lastTime = glfwGetTime();
+       float delta = 0.0f;
+
+       while (!glfwWindowShouldClose(window)) {
+               float time = glfwGetTime();
+               delta = time - lastTime;
+               lastTime = time;
+
+               updateFn(delta);
+
+               renderFrame(window);
+               glfwPollEvents();
+       }
+}
+
+void terminate() {
+       glfwTerminate();
+}
+
+EngineConfig defaultConfig() {
+       return (EngineConfig) { .windowWidth = 800,
+                               .windowHeight = 600,
+                               .windowTitle = newString(NULL),
+                               .swapInterval = 1 };
+}
+
+static void onGlfwError(int error UNUSED, const char* description) {
+       logError("GLFW error: %s", description);
+}
diff --git a/src/engine/engine.h b/src/engine/engine.h
new file mode 100644 (file)
index 0000000..1fdcb01
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef ENGINE_ENGINE_H_
+#define ENGINE_ENGINE_H_
+
+#include "string.h"
+
+typedef struct EngineConfig EngineConfig;
+
+struct EngineConfig {
+       int windowWidth;
+       int windowHeight;
+       String windowTitle;
+       int swapInterval;
+};
+
+void init(EngineConfig);
+void run(void (*updateFn) (float));
+void terminate();
+
+EngineConfig defaultConfig();
+
+#endif // ENGINE_ENGINE_H_
index aa5de049407ae22e922b3b1fe83dbdc1e0d45d45..c37eb4aecaa60ad8f1df2f5b75593ef3e8d4d264 100644 (file)
@@ -1,5 +1,6 @@
 #include "geometry.h"
 
+#include <math.h>
 #include <stddef.h>
 
 Transform identity() {
@@ -12,20 +13,22 @@ Transform identity() {
 Transform multiply(Transform t1, Transform t2) {
        GLfloat* a = (GLfloat*) &t1;
        GLfloat* b = (GLfloat*) &t2;
+       Transform result;
+       GLfloat* c = (GLfloat*) &result;
 
        for (size_t row = 0; row < 4; ++row) {
                for (size_t col = 0; col < 4; ++col) {
-                       b[(row * 4) + col] =
+                       c[(row * 4) + col] =
                                a[(row * 4) + 0] * b[(0 * 4) + col]
                                + a[(row * 4) + 1] * b[(1 * 4) + col]
                                + a[(row * 4) + 2] * b[(2 * 4) + col]
                                + a[(row * 4) + 3] * b[(3 * 4) + col];
                }
        }
-       return t2;
+       return result;
 }
 
-void translate(Transform* transform, Vector3D vec) {
+void translate(Transform* transform, Vector vec) {
        *transform = multiply(
                (Transform) { .a1 = 1.0f, .a2 = 0.0f, .a3 = 0.0f, .a4 = vec.x,
                              .b1 = 0.0f, .b2 = 1.0f, .b3 = 0.0f, .b4 = vec.y,
@@ -34,6 +37,88 @@ void translate(Transform* transform, Vector3D vec) {
                *transform);
 }
 
-Vector3D translationOf(Transform transform) {
-       return (Vector3D) { transform.a4, transform.b4, transform.c4 };
+void rotate(Transform* transform, Vector axis, float angle) {
+       axis = normalized(axis);
+       float l = axis.x;
+       float m = axis.y;
+       float n = axis.z;
+       float sinA = sinf(angle);
+       float cosA = cosf(angle);
+       float omcA = 1 - cosA;
+
+       *transform = multiply(
+               (Transform) { l*l*omcA + cosA, m*l*omcA - n*sinA, n*l*omcA + m*sinA, 0.0f,
+                             l*m*omcA + n*sinA, m*m*omcA + cosA, n*m*omcA - l*sinA, 0.0f,
+                             l*n*omcA - m*sinA, m*n*omcA + l*sinA, n*n*omcA + cosA, 0.0f,
+                             0.0f, 0.0f, 0.0f, 1.0f },
+               *transform);
+}
+
+Vector addVectors(Vector v1, Vector v2){
+       return (Vector) { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
+}
+
+Vector subtractVectors(Vector v1, Vector v2) {
+       return (Vector) { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
+}
+
+Vector crossProduct(Vector v1, Vector v2) {
+       return (Vector) { .x = (v1.y * v2.z) - (v1.z * v2.y),
+                         .y = (v1.z * v2.x) - (v1.x * v2.z),
+                         .z = (v1.x * v2.y) - (v1.y * v2.x) };
+}
+
+float dotProduct(Vector v1, Vector v2) {
+       return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
+}
+
+Vector scaleVector(Vector vec, float scale) {
+       return (Vector) { vec.x * scale,
+                         vec.y * scale,
+                         vec.z * scale };
+}
+
+Vector growVectorNoFlip(Vector vec, float amount) {
+       float mag = magnitude(vec);
+       float factor = (mag + amount) / mag;
+       if (factor < 0.0f) {
+               factor = 0.0f;
+       }
+       return scaleVector(vec, factor);
+}
+
+Vector clampMagnitude(Vector vec, float maxMagnitude) {
+       float m = magnitude(vec);
+       if (m > maxMagnitude) {
+               vec = scaleVector(vec, maxMagnitude / m);
+       }
+       return vec;
+}
+
+float magnitude(Vector vec) {
+       return sqrtf(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
+}
+
+Vector applyTransform(Transform transform, Vector vec) {
+       GLfloat* a = (GLfloat*) &transform;
+       GLfloat b[4] = { vec.x, vec.y, vec.z, 1.0f };
+       GLfloat c[4];
+
+       for (size_t row = 0; row < 4; ++row) {
+               c[row] =
+                       a[(row * 4) + 0] * b[0]
+                       + a[(row * 4) + 1] * b[1]
+                       + a[(row * 4) + 2] * b[2]
+                       + a[(row * 4) + 3] * b[3];
+       }
+       return (Vector) { c[0], c[1], c[2] };
+}
+
+Vector translationOf(Transform transform) {
+       return (Vector) { transform.a4, transform.b4, transform.c4 };
+}
+
+Vector normalized(Vector vec) {
+       float m = magnitude(vec);
+       return (Vector) { vec.x / m, vec.y / m, vec.z / m };
 }
index cea05a64733854b312b4a4ecc80f567a6d0f9ed0..38dd5e6b9ce6ae5597509deaf5d3b66a8987c923 100644 (file)
@@ -1,12 +1,12 @@
-#ifndef GEOMETRY_H_
-#define GEOMETRY_H_
+#ifndef ENGINE_GEOMETRY_H_
+#define ENGINE_GEOMETRY_H_
 
 #include <GL/gl.h>
 
-typedef struct Vector3D Vector3D;
+typedef struct Vector Vector;
 typedef struct Transform Transform;
 
-struct Vector3D {
+struct Vector {
        float x;
        float y;
        float z;
@@ -19,9 +19,22 @@ struct Transform {
        GLfloat d1, d2, d3, d4;
 };
 
+static const float TAU = 6.28318530718f;
+
 Transform identity();
 Transform multiply(Transform t1, Transform t2);
-void translate(Transform* transform, Vector3D vec);
-Vector3D translationOf(Transform transform);
+void translate(Transform* transform, Vector vec);
+void rotate(Transform* transform, Vector axis, float angle);
+Vector addVectors(Vector v1, Vector v2);
+Vector subtractVectors(Vector v1, Vector v2);
+Vector crossProduct(Vector v1, Vector v2);
+float dotProduct(Vector v1, Vector v2);
+Vector scaleVector(Vector vec, float scale);
+Vector growVectorNoFlip(Vector vec, float amount);
+Vector clampMagnitude(Vector vec, float maxMagnitude);
+float magnitude(Vector vec);
+Vector applyTransform(Transform transform, Vector vec);
+Vector translationOf(Transform transform);
+Vector normalized(Vector vec);
 
-#endif // GEOMETRY_H_
+#endif // ENGINE_GEOMETRY_H_
diff --git a/src/engine/input.c b/src/engine/input.c
new file mode 100644 (file)
index 0000000..df33578
--- /dev/null
@@ -0,0 +1,38 @@
+#include "input.h"
+
+#include <stdbool.h>
+
+#include "_prelude.h"
+#include "render.h"
+
+static void (*keyboardInputCallback) (int, int, int, int);
+
+
+
+void onKeyboardEvent(GLFWwindow* window UNUSED, int key, int scancode, int action, int mods) {
+       if (!(mods & GLFW_MOD_CONTROL)) {
+               if (keyboardInputCallback) {
+                       keyboardInputCallback(key, scancode, action, mods);
+               }
+               return;
+       }
+
+       switch (key) {
+               case GLFW_KEY_1:
+                       if (action == GLFW_PRESS) {
+                               debugScene = !debugScene;
+                       }
+                       break;
+               case GLFW_KEY_2:
+                       if (action == GLFW_PRESS) {
+                               debugRender = !debugRender;
+                       }
+                       break;
+               default:
+                       break;
+       }
+}
+
+void setKeyboardInputCallback(void (*callback) (int, int, int, int)) {
+       keyboardInputCallback = callback;
+}
diff --git a/src/engine/input.h b/src/engine/input.h
new file mode 100644 (file)
index 0000000..0a8880b
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef ENGINE_INPUT_H
+#define ENGINE_INPUT_H
+
+#include <GLFW/glfw3.h>
+
+void onKeyboardEvent(GLFWwindow* window, int key, int scancode, int action, int mods);
+void setKeyboardInputCallback(void (*) (int, int, int, int));
+
+#endif // ENGINE_INPUT_H
index 8ba286f1f22bdaf1a9fcbb93f62839d3a4584131..56ddd4c41c1f3eafe43a379ee4d1510cdb30af3b 100644 (file)
@@ -11,7 +11,7 @@ void logMessage(LogLevel msgLevel, const char* func, const char* message, ...) {
        if (msgLevel > logLevel) {
                return;
        }
-       
+
        const char* msgLevelString;
        switch (msgLevel) {
                case LOGLEVEL_ERROR:
@@ -30,13 +30,13 @@ void logMessage(LogLevel msgLevel, const char* func, const char* message, ...) {
                        msgLevelString = "(invalid message level) ";
                        break;
        }
-       
+
        va_list args;
        va_start(args, message);
-       
+
        fprintf(stderr, "%s %s:: ", func, msgLevelString);
        vfprintf(stderr, message, args);
        fputc('\n', stderr);
-       
+
        va_end(args);
 }
index d491f8abbe378e2f628b441f8ffd4fd015fced70..c6dc80825e80d1f16301e9f63071c445c1d995c9 100644 (file)
@@ -1,12 +1,14 @@
-#ifndef LOGGER_H_
-#define LOGGER_H_
+#ifndef ENGINE_LOGGER_H_
+#define ENGINE_LOGGER_H_
 
-typedef enum {
+enum LogLevel {
        LOGLEVEL_ERROR,
        LOGLEVEL_WARNING,
        LOGLEVEL_INFO,
        LOGLEVEL_DEBUG
-} LogLevel;
+};
+
+typedef enum LogLevel LogLevel;
 
 extern LogLevel logLevel;
 
@@ -17,4 +19,4 @@ extern LogLevel logLevel;
 
 void logMessage(LogLevel msgLevel, const char* func, const char* message, ...);
 
-#endif // LOGGER_H_
+#endif // ENGINE_LOGGER_H_
index 0281e6277b26fa4b855251e721851d22e4f343f9..2ff7084cfd1a170ab35404012cebc187694eec89 100644 (file)
@@ -31,16 +31,16 @@ void frameRendered() {
        if (meteringEnabled) {
                ++frames;
                Timepoint now;
-               
+
                if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
                        logWarning("Clock read failed, stopping performance metering");
                        meteringEnabled = false;
                        return;
                }
-               
+
                time_t fullSeconds = now.tv_sec - lastDisplayTime.tv_sec;
                if (now.tv_nsec < lastDisplayTime.tv_nsec) --fullSeconds;
-               
+
                if (fullSeconds > 0) {
                        float seconds = (now.tv_nsec - lastDisplayTime.tv_nsec) / 1000000000.0f;
                        seconds += (float) (now.tv_sec - lastDisplayTime.tv_sec);
index fa47fe2a9d5b53d250d9edad1b89770669318f1f..5f8df7294ce72b0093506b943c35d5845d87fe66 100644 (file)
@@ -1,7 +1,7 @@
-#ifndef PERFORMANCE_H_
-#define PERFORMANCE_H_
+#ifndef ENGINE_PERFORMANCE_H_
+#define ENGINE_PERFORMANCE_H_
 
 void initPerformanceMetering();
 void frameRendered();
 
-#endif // PERFORMANCE_H_
+#endif // ENGINE_PERFORMANCE_H_
index 0281087656c264b66e800efe7fd69e31b50136e3..5d43e16c189a1c95573dc27671d735ce9ef1b857 100644 (file)
@@ -1,16 +1,14 @@
 #include "render.h"
 
-#include <stdbool.h>
-#include <GL/freeglut_std.h>
-#define  GL_GLEXT_PROTOTYPES
-#include <GL/glext.h>
-#undef GL_GLEXT_PROTOTYPES
-
 #include "geometry.h"
 #include "performance.h"
 
+#include "game/player.h"
+
 float viewportAspectRatio = 1.0f;
 const Scene* cameraAnchor;
+bool debugScene = false;
+bool debugRender = false;
 
 static const float AXIS_RADIUS = 5.0f;
 
@@ -24,23 +22,25 @@ static void drawSolid(const Solid* solid);
 
 void initRender() {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-       
+
        GLfloat light0_ambient[] = {0.1f, 0.1f, 0.1f, 1.0f};
        GLfloat light0_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
        GLfloat light0_specular[] = {0.96f, 0.98f, 1.0f, 1.0f};
        GLfloat light0_position[] = {5.0f, 10.0f, 5.0f, 0.0f}; // (w == 0.0f) == directional
-       
+
        glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
        glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
        glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
-       
+
        glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f);
        glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05f);
        glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.005f);
+
+       //glShadeModel(GL_FLAT);
 }
 
-void renderFrame() {
+void renderFrame(GLFWwindow* window) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        glEnable(GL_NORMALIZE);
@@ -53,9 +53,8 @@ void renderFrame() {
        renderScene(currentScene, identity());
 
        glFlush();
-       glutSwapBuffers();
+       glfwSwapBuffers(window);
        frameRendered();
-       glutPostRedisplay();
 }
 
 static void renderScene(const Scene* scene, const Transform baseTransform) {
@@ -69,7 +68,11 @@ static void renderScene(const Scene* scene, const Transform baseTransform) {
        glLoadMatrixf((const GLfloat*) &transform);
 
        glDisable(GL_LIGHTING);
-       drawAxes();
+
+       if (debugScene || scene == playerProjectedMovement) {
+               drawAxes();
+       }
+
        glEnable(GL_LIGHTING);
 
        if (scene->solid) {
@@ -100,7 +103,7 @@ static void setupCamera() {
 
 static void moveCameraTo(const Scene* anchor) {
        glMatrixMode(GL_PROJECTION);
-       Vector3D pos = translationOf(worldTransform(anchor));
+       Vector pos = translationOf(worldTransform(anchor));
        glTranslatef(-pos.x, -pos.y, -pos.z);
 }
 
@@ -126,24 +129,43 @@ static void drawAxes() {
        glEnd();
 }
 
+static GLfloat absolute(GLfloat a) {
+       return a < 0 ? -a : a;
+}
+
 static void drawSolid(const Solid* solid) {
        if (solid == NULL) {
                return;
        }
-       
+
        glMatrixMode(GL_MODELVIEW);
        glColor3f(0.5f, 1.0f, 0.0f);
-       
+
        for (size_t meshIndex = 0; meshIndex < solid->numMeshes; ++meshIndex) {
                const Mesh mesh = solid->meshes[meshIndex];
                glBindTexture(GL_TEXTURE_2D,
                              solid->materials[mesh.materialIndex].textureId);
-               bool hasNormals = mesh.normals != NULL;
-               bool hasTextureCoords = mesh.textureCoords != NULL;
-               
+
                for (size_t faceIndex = 0; faceIndex < mesh.numFaces; ++faceIndex) {
                        const Face face = mesh.faces[faceIndex];
-                       
+
+                       if (debugRender && face.normals) {
+                               glDisable(GL_LIGHTING);
+                               glDisable(GL_TEXTURE_2D);
+                               glBegin(GL_LINES);
+                               for (size_t i = 0; i < face.numIndices; ++i) {
+                                       size_t vertIndex = face.indices[i];
+                                       Vector vertex = mesh.vertices[vertIndex];
+                                       Vector normal = face.normals[i];
+                                       glColor3f(absolute(normal.x), absolute(normal.y), absolute(normal.z));
+                                       glVertex3f(vertex.x, vertex.y, vertex.z);
+                                       glVertex3f(vertex.x + normal.x, vertex.y + normal.y, vertex.z + normal.z);
+                               }
+                               glEnd();
+                               glEnable(GL_TEXTURE_2D);
+                               glEnable(GL_LIGHTING);
+                       }
+
                        GLenum faceMode;
                        switch (face.numIndices) {
                                case 1: faceMode = GL_POINTS; break;
@@ -151,23 +173,24 @@ static void drawSolid(const Solid* solid) {
                                case 3: faceMode = GL_TRIANGLES; break;
                                default: faceMode = GL_POLYGON; break;
                        }
-                       
+
                        glBegin(faceMode);
-                       
+
                        for (size_t i = 0; i < face.numIndices; ++i) {
                                size_t vertIndex = face.indices[i];
-                               if (hasNormals) {
-                                       if (hasTextureCoords) {
-                                               Vector3D coords = mesh.textureCoords[vertIndex];
+                               if (face.normals) {
+                                       if (mesh.textureCoords) {
+                                               Vector coords = mesh.textureCoords[vertIndex];
                                                glTexCoord2f(coords.x, coords.y);
                                        }
-                                       Vector3D normal = mesh.normals[vertIndex];
+                                       Vector normal = face.normals[i];
                                        glNormal3f(normal.x, normal.y, normal.z);
                                }
-                               Vector3D vertex = mesh.vertices[vertIndex];
+
+                               Vector vertex = mesh.vertices[vertIndex];
                                glVertex3f(vertex.x, vertex.y, vertex.z);
                        }
-                       
+
                        glEnd();
                }
        }
index 25ea9beccbf548ef21a2a799e5221011d7f58f02..e916360eb6ece4300c19d49e324ec48ab8045152 100644 (file)
@@ -1,12 +1,20 @@
-#ifndef RENDER_H_
-#define RENDER_H_
+#ifndef ENGINE_RENDER_H_
+#define ENGINE_RENDER_H_
+
+#include <stdbool.h>
+#include <GLFW/glfw3.h>
+#define  GL_GLEXT_PROTOTYPES
+#include <GL/glext.h>
+#undef GL_GLEXT_PROTOTYPES
 
 #include "scene.h"
 
 extern float viewportAspectRatio;
 extern const Scene* cameraAnchor;
+extern bool debugScene;
+extern bool debugRender;
 
 void initRender();
-void renderFrame();
+void renderFrame(GLFWwindow* window);
 
-#endif // RENDER_H_
+#endif // ENGINE_RENDER_H_
index f603c147ab0462616b3a9be19d3612c2e3e4931a..895e0352433a48b328ed67482983778758b1ae0f 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef SCENE_H_
-#define SCENE_H_
+#ifndef ENGINE_SCENE_H_
+#define ENGINE_SCENE_H_
 
 #include "asset.h"
 
@@ -20,4 +20,4 @@ void insertChildScene(Scene* scene, Scene* newChild);
 void reparentScene(Scene* scene, Scene* newParent);
 Transform worldTransform(const Scene* scene);
 
-#endif // SCENE_H_
+#endif // ENGINE_SCENE_H_
diff --git a/src/engine/string.c b/src/engine/string.c
new file mode 100644 (file)
index 0000000..04ad0db
--- /dev/null
@@ -0,0 +1,33 @@
+#include "string.h"
+
+#include <stdlib.h>
+
+String newString(const char* s) {
+       size_t len = 0u;
+       char* cstr;
+       if (s) {
+               len = strlen(s);
+               cstr = memcpy(malloc((len + 1) * sizeof(char)),
+                             s,
+                             len * sizeof(char));
+       }
+       else {
+               cstr = malloc(1 * sizeof(char));
+       }
+       cstr[len] = '\0';
+       return (String) { .length = len, .cstr = cstr };
+}
+
+String stringFromAiString(const struct aiString aistr) {
+       char* cstr = memcpy(malloc((aistr.length + 1) * sizeof(char)),
+                               aistr.data,
+                               aistr.length * sizeof(char));
+       cstr[aistr.length] = '\0';
+       return (String) { .length = aistr.length, .cstr = cstr };
+}
+
+void dropString(String str) {
+       free(str.cstr);
+       str.length = 0u;
+       str.cstr = NULL;
+}
diff --git a/src/engine/string.h b/src/engine/string.h
new file mode 100644 (file)
index 0000000..92409c3
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef ENGINE_STRING_H_
+#define ENGINE_STRING_H_
+
+#include <assimp/types.h>
+
+typedef struct String String;
+
+struct String {
+       size_t length;
+       char* cstr;
+};
+
+String newString(const char* s);
+String stringFromAiString(const struct aiString aistr);
+void dropString(String str);
+
+#endif // ENGINE_STRING_H_
index 53ff29a2ee77ad86b750ba6f4bf5f1ee20a59e0f..8f465f3ff81bbd7efbebe58af30605f7dbfa7917 100644 (file)
@@ -10,17 +10,17 @@ TgaImage* readTga(const char* path) {
        if (tgaFile == NULL) {
                return NULL;
        }
-       
+
        TgaHeader header;
-       
+
        if (fread(&header, sizeof(TgaHeader), 1, tgaFile) != 1) {
                fclose(tgaFile);
                return NULL;
        }
-       
+
        GLenum imageFormat;
        GLint imageComponents;
-       
+
        switch (header.imageBpp) {
                case 32:
                        imageFormat = GL_BGRA;
@@ -38,32 +38,32 @@ TgaImage* readTga(const char* path) {
                        fclose(tgaFile);
                        return NULL;
        }
-       
+
        unsigned long imageSize = header.imageWidth * header.imageHeight * (header.imageBpp >> 3);
-       
+
        GLbyte* bytes = malloc(imageSize * sizeof(GLbyte));
        if (bytes == NULL) {
                fclose(tgaFile);
                return NULL;
        }
-       
+
        if (fread(bytes, imageSize, 1, tgaFile) != 1) {
                free(bytes);
                fclose(tgaFile);
                return NULL;
        }
-       
+
        fclose(tgaFile);
-       
+
        TgaImage* image = malloc(sizeof(TgaImage));
        if (image == NULL) {
                return NULL;
        }
-       
+
        (*image).header = header;
        (*image).imageFormat = imageFormat;
        (*image).imageComponents = imageComponents;
        (*image).bytes = bytes;
-       
+
        return image;
 }
index d044362a24b03dc7851e9ccb4f3b76a9de8c464b..01e9ca88c10664c29f8b472f0573348799b8bd96 100644 (file)
@@ -1,10 +1,13 @@
-#ifndef TGA_H_
-#define TGA_H_
+#ifndef ENGINE_TGA_H_
+#define ENGINE_TGA_H_
 
 #include <GL/gl.h>
 
+typedef struct TgaHeader TgaHeader;
+typedef struct TgaImage TgaImage;
+
 #pragma pack(push, 1)
-typedef struct {
+struct TgaHeader {
        GLubyte idLength;
        GLbyte colorMapType;
        GLbyte imageType;
@@ -17,16 +20,16 @@ typedef struct {
        GLushort imageHeight;
        GLubyte imageBpp;
        GLbyte imageDescriptor;
-} TgaHeader;
+};
 #pragma pack(pop)
 
-typedef struct {
+struct TgaImage {
        TgaHeader header;
        GLenum imageFormat;
        GLint imageComponents;
        GLbyte* bytes;
-} TgaImage;
+};
 
 TgaImage* readTga(const char* path);
 
-#endif // TGA_H_
+#endif // ENGINE_TGA_H_
index 4b06f0ce62f0fe8b1ea90a0a928a1dde262c6591..3bd283a17be5efa0af881ec9a057c33e8ac3a08f 100644 (file)
@@ -1,12 +1,13 @@
 #include "ui.h"
 
+#include "_prelude.h"
 #include "render.h"
 
-void resizeStage(GLsizei width, GLsizei height) {
+void resizeStage(GLFWwindow* window UNUSED, int width, int height) {
        if (height == 0)
                height = 1;
-       
+
        glViewport(0, 0, width, height);
-       
+
        viewportAspectRatio = (float) width / (float) height;
 }
index 1e1cedfa6a3ac6e5b10c083dee5b227f78ac8308..7e33b4f1a23df5f0a0ac004fcdac123d79d40c8b 100644 (file)
@@ -1,8 +1,10 @@
-#ifndef UI_H_
-#define UI_H_
+#ifndef ENGINE_UI_H_
+#define ENGINE_UI_H_
 
 #include <GL/gl.h>
 
-void resizeStage(GLsizei width, GLsizei height);
+typedef struct GLFWwindow GLFWwindow;
 
-#endif // UI_H_
+void resizeStage(GLFWwindow* window, int width, int height);
+
+#endif // ENGINE_UI_H_
diff --git a/src/game/game.c b/src/game/game.c
new file mode 100644 (file)
index 0000000..25b1c30
--- /dev/null
@@ -0,0 +1,19 @@
+#include "game.h"
+
+#include "engine/input.h"
+
+#include "input.h"
+#include "level.h"
+#include "player.h"
+
+void initGame() {
+       initLevel();
+       initPlayer();
+       startLevel();
+
+       setKeyboardInputCallback(keyboardInput);
+}
+
+void update(float delta) {
+       updatePlayer(delta);
+}
diff --git a/src/game/game.h b/src/game/game.h
new file mode 100644 (file)
index 0000000..7742846
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef GAME_H_
+#define GAME_H_
+
+void initGame();
+void update(float delta);
+
+#endif // GAME_H_
diff --git a/src/game/input.c b/src/game/input.c
new file mode 100644 (file)
index 0000000..089291a
--- /dev/null
@@ -0,0 +1,46 @@
+#include "input.h"
+
+#include <GLFW/glfw3.h>
+
+#include "engine/_prelude.h"
+
+#include "player.h"
+
+void keyboardInput(int key, int scancode UNUSED, int action, int mods UNUSED) {
+       switch (key) {
+               case GLFW_KEY_W:
+                       if (action == GLFW_PRESS) {
+                               startMovement(DIRECTION_UP);
+                       }
+                       else if (action == GLFW_RELEASE) {
+                               stopMovement(DIRECTION_UP);
+                       }
+                       break;
+               case GLFW_KEY_S:
+                       if (action == GLFW_PRESS) {
+                               startMovement(DIRECTION_DOWN);
+                       }
+                       else if (action == GLFW_RELEASE) {
+                               stopMovement(DIRECTION_DOWN);
+                       }
+                       break;
+               case GLFW_KEY_A:
+                       if (action == GLFW_PRESS) {
+                               startMovement(DIRECTION_LEFT);
+                       }
+                       else if (action == GLFW_RELEASE) {
+                               stopMovement(DIRECTION_LEFT);
+                       }
+                       break;
+               case GLFW_KEY_D:
+                       if (action == GLFW_PRESS) {
+                               startMovement(DIRECTION_RIGHT);
+                       }
+                       else if (action == GLFW_RELEASE) {
+                               stopMovement(DIRECTION_RIGHT);
+                       }
+                       break;
+               default:
+                       break;
+       }
+}
diff --git a/src/game/input.h b/src/game/input.h
new file mode 100644 (file)
index 0000000..a354574
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef INPUT_H_
+#define INPUT_H_
+
+void keyboardInput(int key, int scancode, int action, int mods);
+
+#endif // INPUT_H_
index 2b45eecdc2839a9dbb480e79ffea548b4f3e428b..b9a6b5884ec16b72bceaf0bfb64b45510cc90f05 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "engine/logger.h"
 #include "engine/scene.h"
+#include "engine/tga.h"
 
 #include "player.h"
 
@@ -12,18 +13,20 @@ BlockGrid levelGrid;
 
 static Block blockEmpty = { .type = BLOCKTYPE_SPACE,
                             .solid = NULL };
-static Block blockWall01 = { .type = BLOCKTYPE_OBSTACLE,
+static Block blockWall01 = { .type = BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z,
                              .solid = NULL };
 
 static Transform playerSpawnTransform;
 
+static void buildLevelFromImage(const TgaImage* image);
+
 
 
 void initLevel() {
        playerSpawnTransform = identity();
-       translate(&playerSpawnTransform, (Vector3D) { .x = -BLOCKGRID_CELL_SIZE,
-                                                     .y = 0.0f,
-                                                     .z = -BLOCKGRID_CELL_SIZE });
+       translate(&playerSpawnTransform, (Vector) { -BLOCKGRID_CELL_SIZE,
+                                                   0.0f,
+                                                   -BLOCKGRID_CELL_SIZE });
 
        blockWall01.solid = importSolid("assets/wall01.3ds");
 
@@ -34,9 +37,10 @@ void initLevel() {
        for (size_t z = 0; z < levelGrid.depth; ++z) {
                for (size_t x = 0; x < levelGrid.width; ++x) {
                        Scene* blockScene = newScene();
-                       translate(&blockScene->transform, (Vector3D) { .x = x * BLOCKGRID_CELL_SIZE,
-                                                                      .y = 0.0f,
-                                                                      .z = z * BLOCKGRID_CELL_SIZE });
+                       translate(&blockScene->transform, (Vector) {
+                               (x * BLOCKGRID_CELL_SIZE) + (BLOCKGRID_CELL_SIZE * 0.5f),
+                               0.0f,
+                               (z * BLOCKGRID_CELL_SIZE) + (BLOCKGRID_CELL_SIZE * 0.5f) });
                        blockScene->solid = getBlockFromGrid(levelGrid, x, z)->solid;
                        insertChildScene(levelScene, blockScene);
                }
@@ -49,28 +53,28 @@ void startLevel() {
        spawnPlayer(playerSpawnTransform);
 }
 
-void buildLevelFromImage(TgaImage* image) {
+static void buildLevelFromImage(const TgaImage* image) {
        if (image == NULL) {
                logError("Null image received, cannot build level");
                return;
        }
-       
+
        if (image->header.imageBpp != 32) {
                logError("Invalid level image format (%d bpp)", image->header.imageBpp);
                return;
        }
-       
+
        BlockGrid newGrid = { .width = image->header.imageWidth,
                              .depth = image->header.imageHeight,
                              .blocks = malloc(image->header.imageWidth
                                               * image->header.imageHeight
                                               * sizeof(Block*)) };
-       
+
        for (size_t row = 0; row < newGrid.depth; ++row) {
                for (size_t x = 0; x < newGrid.width; ++x) {
                        // Flip the image vertically due to (0, 0) being bottom left
                        size_t z = newGrid.depth - row - 1;
-                       
+
                        uint32_t pixelColorARGB = ((uint32_t*) image->bytes)[(row * newGrid.width) + x];
                        Block* block;
                        switch (pixelColorARGB) {
@@ -80,9 +84,10 @@ void buildLevelFromImage(TgaImage* image) {
                                case 0xFF00FFFF:
                                        block = &blockEmpty;
                                        playerSpawnTransform = identity();
-                                       translate(&playerSpawnTransform, (Vector3D) { .x = x * BLOCKGRID_CELL_SIZE,
-                                                                                     .y = 0.0f,
-                                                                                     .z = z * BLOCKGRID_CELL_SIZE });
+                                       translate(&playerSpawnTransform, (Vector) {
+                                               (x * BLOCKGRID_CELL_SIZE) + (BLOCKGRID_CELL_SIZE * 0.5f),
+                                               0.0f,
+                                               (z * BLOCKGRID_CELL_SIZE) + (BLOCKGRID_CELL_SIZE * 0.5f) });
                                        break;
                                default:
                                        block = &blockEmpty;
@@ -91,6 +96,69 @@ void buildLevelFromImage(TgaImage* image) {
                        setBlockInGrid(newGrid, x, z, block);
                }
        }
-       
+
        levelGrid = newGrid;
 }
+
+static inline size_t nonNegative(long n) {
+       return n < 0 ? 0u : n;
+}
+
+GridLocation gridLocationFromPosition(Vector pos) {
+       Vector scaledPos = scaleVector(pos, 1.0f / BLOCKGRID_CELL_SIZE);
+       return (GridLocation) { .x = nonNegative(scaledPos.x),
+                               .z = nonNegative(scaledPos.z) };
+}
+
+Obstacle getObstacles(GridLocation loc) {
+       Obstacle result = OBSTACLE_NONE;
+       if (loc.x == 0) {
+               result |= OBSTACLE_XN | OBSTACLE_XN_ZP | OBSTACLE_XN_ZN;
+       }
+       if (loc.x >= levelGrid.width - 1) {
+               result |= OBSTACLE_XP | OBSTACLE_XP_ZP | OBSTACLE_XP_ZN;
+       }
+       if (loc.z == 0) {
+               result |= OBSTACLE_ZN | OBSTACLE_XP_ZN | OBSTACLE_XN_ZN;
+       }
+       if (loc.z >= levelGrid.depth - 1) {
+               result |= OBSTACLE_ZP | OBSTACLE_XP_ZP | OBSTACLE_XN_ZP;
+       }
+       if (!(result & OBSTACLE_XP)
+           && getBlockFromGrid(levelGrid, loc.x + 1, loc.z)->type & BLOCKTYPE_OBSTACLE_X) {
+               result |= OBSTACLE_XP;
+       }
+       if (!(result & OBSTACLE_XN)
+           && getBlockFromGrid(levelGrid, loc.x - 1, loc.z)->type & BLOCKTYPE_OBSTACLE_X) {
+               result |= OBSTACLE_XN;
+       }
+       if (!(result & OBSTACLE_ZP)
+           && getBlockFromGrid(levelGrid, loc.x, loc.z + 1)->type & BLOCKTYPE_OBSTACLE_Z) {
+               result |= OBSTACLE_ZP;
+       }
+       if (!(result & OBSTACLE_ZN)
+           && getBlockFromGrid(levelGrid, loc.x, loc.z - 1)->type & BLOCKTYPE_OBSTACLE_Z) {
+               result |= OBSTACLE_ZN;
+       }
+       if (!(result & OBSTACLE_XP_ZP)
+           && getBlockFromGrid(levelGrid, loc.x + 1, loc.z + 1)->type
+              & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
+               result |= OBSTACLE_XP_ZP;
+       }
+       if (!(result & OBSTACLE_XP_ZN)
+           && getBlockFromGrid(levelGrid, loc.x + 1, loc.z - 1)->type
+              & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
+               result |= OBSTACLE_XP_ZN;
+       }
+       if (!(result & OBSTACLE_XN_ZP)
+           && getBlockFromGrid(levelGrid, loc.x - 1, loc.z + 1)->type
+              & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
+               result |= OBSTACLE_XN_ZP;
+       }
+       if (!(result & OBSTACLE_XN_ZN)
+           && getBlockFromGrid(levelGrid, loc.x - 1, loc.z - 1)->type
+              & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
+               result |= OBSTACLE_XN_ZN;
+       }
+       return result;
+}
index 4d6992ede5631eabcc79fb5190732e1ea75194d7..cca8a8e469202e0a54e8c52a8486c4ac80e7f062 100644 (file)
@@ -4,33 +4,55 @@
 #include <stdint.h>
 
 #include "engine/asset.h"
-#include "engine/tga.h"
 
-typedef enum {
-       BLOCKTYPE_SPACE,
-       BLOCKTYPE_OBSTACLE_X,
-       BLOCKTYPE_OBSTACLE_Z,
-       BLOCKTYPE_OBSTACLE
-} BlockType;
+enum BlockType {
+       BLOCKTYPE_SPACE = 0,
+       BLOCKTYPE_OBSTACLE_X = 1 << 0,
+       BLOCKTYPE_OBSTACLE_Z = 1 << 1
+};
 
-typedef struct {
-       const BlockType type;
+enum Obstacle {
+       OBSTACLE_NONE = 0,
+       OBSTACLE_XP = 1 << 0,
+       OBSTACLE_XN = 1 << 1,
+       OBSTACLE_ZP = 1 << 2,
+       OBSTACLE_ZN = 1 << 3,
+       OBSTACLE_XP_ZP = 1 << 4,
+       OBSTACLE_XP_ZN = 1 << 5,
+       OBSTACLE_XN_ZP = 1 << 6,
+       OBSTACLE_XN_ZN = 1 << 7
+};
+
+typedef enum BlockType BlockType;
+typedef enum Obstacle Obstacle;
+typedef struct Block Block;
+typedef struct BlockGrid BlockGrid;
+typedef struct GridLocation GridLocation;
+
+struct Block {
+       BlockType type;
        const Solid* solid;
-} Block;
+};
 
-typedef struct {
+struct BlockGrid {
        size_t width;
        size_t depth;
        Block** blocks;
-} BlockGrid;
+};
+
+struct GridLocation {
+       size_t x;
+       size_t z;
+};
 
-#define BLOCKGRID_CELL_SIZE 2.5f
+static const float BLOCKGRID_CELL_SIZE = 2.5f;
 
 extern BlockGrid levelGrid;
 
 void initLevel();
 void startLevel();
-void buildLevelFromImage(TgaImage* image);
+GridLocation gridLocationFromPosition(Vector pos);
+Obstacle getObstacles(GridLocation loc);
 
 static inline Block* getBlockFromGrid(BlockGrid grid, size_t x, size_t z) {
        return grid.blocks[(z * grid.width) + x];
@@ -40,4 +62,8 @@ static inline void setBlockInGrid(BlockGrid grid, size_t x, size_t z, Block* blo
        grid.blocks[(z * grid.width) + x] = block;
 }
 
+static inline float cellBoundaryCoord(size_t cellIndex) {
+       return cellIndex * BLOCKGRID_CELL_SIZE;
+}
+
 #endif // LEVEL_H_
index 2b091fde62d266fb6448abedce823bec6f32478e..636b130603dfc820218d5898150bad36af2049f2 100644 (file)
 #include "engine/asset.h"
 #include "engine/render.h"
 
+#include "level.h"
+
+static const float movementSpeed = 0.5f;
+static const float collisionRadius = 0.5f;
+
 Scene* playerCharacter;
+Scene* playerProjectedMovement;
+static Transform screenToWorldMovementTransform;
+static Vector worldMovementUp;
+static Vector worldMovementDown;
+static Vector worldMovementLeft;
+static Vector worldMovementRight;
+static Direction movementDirection;
+
+static void movePlayer(Vector direction, float delta);
+static Vector worldMovementDirection(float x, float y);
 
 
 
 void initPlayer() {
+       screenToWorldMovementTransform = identity();
+       rotate(&screenToWorldMovementTransform, (Vector) { 0.0f, 1.0f, 0.0f }, - TAU / 8.0f);
+
+       worldMovementUp = worldMovementDirection(0.0f, 1.0f);
+       worldMovementDown = worldMovementDirection(0.0f, -1.0f);
+       worldMovementLeft = worldMovementDirection(-1.0f, 0.0f);
+       worldMovementRight = worldMovementDirection(1.0f, 0.0f);
+
        playerCharacter = newScene();
        cameraAnchor = playerCharacter;
        playerCharacter->solid = importSolid("assets/playercharacter.3ds");
+
+       playerProjectedMovement = newScene();
 }
 
 void spawnPlayer(Transform transform) {
        playerCharacter->transform = transform;
        reparentScene(playerCharacter, currentScene);
+       reparentScene(playerProjectedMovement, currentScene);
+}
+
+void updatePlayer(float delta) {
+       Vector direction = { 0.0f, 0.0f, 0.0f };
+       if (movementDirection & DIRECTION_UP) {
+               direction = addVectors(direction, worldMovementUp);
+       }
+       if (movementDirection & DIRECTION_DOWN) {
+               direction = addVectors(direction, worldMovementDown);
+       }
+       if (movementDirection & DIRECTION_LEFT) {
+               direction = addVectors(direction, worldMovementLeft);
+       }
+       if (movementDirection & DIRECTION_RIGHT) {
+               direction = addVectors(direction, worldMovementRight);
+       }
+       movePlayer(direction, delta);
+}
+
+void startMovement(Direction direction) {
+       movementDirection |= direction;
+}
+
+void stopMovement(Direction direction) {
+       movementDirection &= ~direction;
+}
+
+static void movePlayer(Vector direction, float delta) {
+       direction = clampMagnitude(direction, 1.0f);
+       Vector displacement = scaleVector(direction, delta * movementSpeed);
+
+{
+Vector displacement = scaleVector(direction, 0.006944f * movementSpeed * 1000.0f);
+
+       playerProjectedMovement->transform = playerCharacter->transform;
+
+       Vector initialPosition = translationOf(playerCharacter->transform);
+       Vector position = initialPosition;
+
+       GridLocation location = gridLocationFromPosition(position);
+       Obstacle obstacle = getObstacles(location);
+
+       // Eliminate redundant corner checks
+       if (obstacle & OBSTACLE_XP) {
+               obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN);
+       }
+       if (obstacle & OBSTACLE_XN) {
+               obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN);
+       }
+       if (obstacle & OBSTACLE_ZP) {
+               obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP);
+       }
+       if (obstacle & OBSTACLE_ZN) {
+               obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN);
+       }
+
+       float edgeXp = cellBoundaryCoord(location.x + 1);
+       float edgeXn = cellBoundaryCoord(location.x);
+       float edgeZp = cellBoundaryCoord(location.z + 1);
+       float edgeZn = cellBoundaryCoord(location.z);
+       float distanceXp = edgeXp - position.x;
+       if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius;
+       float distanceXn = edgeXn - position.x;
+       if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius;
+       float distanceZp = edgeZp - position.z;
+       if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius;
+       float distanceZn = edgeZn - position.z;
+       if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius;
+
+       // Check all edges for intersecting already
+       if (distanceXp < 0.0f) {
+               position.x += distanceXp;
+               displacement = growVectorNoFlip(displacement, distanceXp);
+               distanceXp = 0.0f;
+       }
+       if (distanceXn > 0.0f) {
+               position.x += distanceXn;
+               displacement = growVectorNoFlip(displacement, - distanceXn);
+               distanceXn = 0.0f;
+       }
+       if (distanceZp < 0.0f) {
+               position.z += distanceZp;
+               displacement = growVectorNoFlip(displacement, distanceZp);
+               distanceZp = 0.0f;
+       }
+       if (distanceZn > 0.0f) {
+               position.z += distanceZn;
+               displacement = growVectorNoFlip(displacement, - distanceZn);
+               distanceZn = 0.0f;
+       }
+
+       // Calculate direct movement limits
+       float dx = displacement.x;
+       float dz = displacement.z;
+       if (dx > distanceXp) {
+               dz *= distanceXp / dx;
+               dx = distanceXp;
+               // Might need a flag that we've reached a limit in X+?
+       }
+       if (dx < distanceXn) {
+               dz *= distanceXn / dx;
+               dx = distanceXn;
+               // ?
+       }
+       if (dz > distanceZp) {
+               dx *= distanceZp / dz;
+               dz = distanceZp;
+               // ?
+       }
+       if (dz < distanceZn) {
+               dx *= distanceZn / dz;
+               dz = distanceZn;
+               // ?
+       }
+       // This is how far we can move until we collide or leave the grid cell
+       position = addVectors(position, (Vector) { dx, 0.0f, dz });
+       translate(&playerProjectedMovement->transform, subtractVectors(position, initialPosition));
+}
+
+
+       translate(&playerCharacter->transform, displacement);
+}
+
+static Vector worldMovementDirection(float x, float y) {
+       Vector direction = (Vector) { x, 0.0f, -y };
+       direction = normalized(
+               applyTransform(screenToWorldMovementTransform, direction));
+       return direction;
 }
index 184406998c30e38c15819318b339133bf444b879..2515d1da4538f1596559f157f1f2f7543f1e0eb5 100644 (file)
@@ -5,9 +5,22 @@
 
 #include "engine/scene.h"
 
+enum Direction {
+       DIRECTION_UP = 1 << 0,
+       DIRECTION_DOWN = 1 << 1,
+       DIRECTION_LEFT = 1 << 2,
+       DIRECTION_RIGHT = 1 << 3,
+};
+
+typedef enum Direction Direction;
+
 extern Scene* playerCharacter;
+extern Scene* playerProjectedMovement;
 
 void initPlayer();
 void spawnPlayer(Transform transform);
+void updatePlayer(float delta);
+void startMovement(Direction direction);
+void stopMovement(Direction direction);
 
 #endif // PLAYER_H_
index a84730ec7b9f589518c698324ecd9f3d5a1b03d6..6cd21f0077b962b69783a200b7dea9eae43a47fa 100644 (file)
@@ -1,65 +1,22 @@
-//#include <GL/glxew.h>
-#include <GL/freeglut_std.h>
+#include "engine/engine.h"
 
-#include "engine/logger.h"
-#include "engine/performance.h"
-#include "engine/render.h"
-#include "engine/ui.h"
+#include "game/game.h"
 
-#include "game/level.h"
-#include "game/player.h"
+int main(/*int argc, char** argv*/) {
+       EngineConfig cfg = { .windowWidth = 1280,
+                            .windowHeight = 720,
+                            .windowTitle = newString("shadowclad"),
+                            .swapInterval = 1 };
 
-int main(int argc, char** argv) {
-       glutInit(&argc, argv);
-       // glutInitContextVersion(4,5); TODO establish correct context
-       
-       glutInitWindowSize(1280, 720);
-       
-       glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
-       glutCreateWindow("shadowclad");
-       
-       logInfo("OpenGL %s", (const char*) glGetString(GL_VERSION));
-       //logInfo("GLSL %s", (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
-       logInfo("%s", (const char*) glGetString(GL_RENDERER));
-       /*
-       GLenum glewInitStatus = glewInit();
-       if (glewInitStatus != GLEW_OK) {
-               logError("GLEW init failed: %s", (const char*) glewGetErrorString(glewInitStatus));
-               return 1;
-       }
-       logInfo("GLEW %s", (const char*) glewGetString(GLEW_VERSION));
-       
-       if (GLXEW_EXT_swap_control) {
-               Display* display = glXGetCurrentDisplay();
-               GLXDrawable drawable = glXGetCurrentDrawable();
-               if (drawable) {
-                       glXSwapIntervalEXT(display, drawable, 1);
-               }
-               else {
-                       logWarning("Drawable is not here ¯\\_(ツ)_/¯");
-                       logWarning("Could not enable vsync (GLX_EXT_swap_control)");
-               }
-       }
-       else if (GLXEW_MESA_swap_control) {
-               glXSwapIntervalMESA(1);
-               logDebug("Vsync enabled with GLX_MESA_swap_control, swap interval %d", glXGetSwapIntervalMESA());
-       }
-       else {
-               logWarning("Could not enable vsync (extensions not supported)");
-       }
-       */
-       glutDisplayFunc(renderFrame);
-       glutReshapeFunc(resizeStage);
-       //glutKeyboardFunc(key_pressed);
-       //glutMouseFunc(mouse_button_event);
-       //glutMotionFunc(mouse_motion_event);
-       
-       initRender();
-       //initPerformanceMetering();
-       initLevel();
-       initPlayer();
-       startLevel();
-       
-       glutMainLoop();
+       // Engine startup
+       init(cfg);
+
+       initGame();
+
+       // Main update loop
+       run(update);
+
+       // Shutdown
+       terminate();
        return 0;
 }