diff --git a/include/engine/camera.h b/include/engine/camera.h index b2e61cd..af2b8d1 100644 --- a/include/engine/camera.h +++ b/include/engine/camera.h @@ -4,6 +4,7 @@ #include "utils/point3.h" #include "utils/vector3.h" +#include "deps/tinyxml2.h" class Camera { @@ -20,6 +21,8 @@ class Camera Camera(const Point3 &position, const Point3 &lookAt, const Vector3 &up, float fov, float near, float far); + explicit Camera(tinyxml2::XMLElement *cameraElement); + void place(); void reactKey(unsigned char key, int x, int y); diff --git a/include/engine/group.h b/include/engine/group.h index 8f7427d..d1ff7bf 100644 --- a/include/engine/group.h +++ b/include/engine/group.h @@ -4,23 +4,24 @@ #include #include -#include "utils/model.h" #include "engine/transform.h" +#include "engine/model.h" +#include "deps/tinyxml2.h" class Group { private: - std::vector> transforms; + std::vector> transforms; std::vector models; std::vector subgroups; public: Group() = default; - Group(std::vector> transforms, std::vector models, std::vector subgroups) + Group(std::vector> transforms, std::vector models, std::vector subgroups) : transforms(std::move(transforms)), models(std::move(models)), subgroups(std::move(subgroups)) {} - void initBuffers(); + explicit Group(tinyxml2::XMLElement *groupElement); void draw(float time) const; }; diff --git a/include/engine/model.h b/include/engine/model.h new file mode 100644 index 0000000..08462f6 --- /dev/null +++ b/include/engine/model.h @@ -0,0 +1,26 @@ +#ifndef CG2324_ENGINE_MODEL_H +#define CG2324_ENGINE_MODEL_H + + +#include "utils/point3.h" + +#include +#include +#include + +class Model +{ +private: + GLuint buffer{}; + GLsizei size{}; + +public: + Model() = default; + + explicit Model(const std::string &filename); + + void draw() const; +}; + + +#endif //CG2324_ENGINE_MODEL_H diff --git a/include/engine/transform.h b/include/engine/transform.h index 33de03d..be7a8e2 100644 --- a/include/engine/transform.h +++ b/include/engine/transform.h @@ -6,87 +6,99 @@ #include "utils/vector3.h" #include "utils/point3.h" -class Transform +namespace transform { -public: - virtual ~Transform() = default; + class Transform + { + public: + virtual ~Transform() = default; - virtual void apply(float time) noexcept = 0; -}; + virtual void apply(float time) noexcept = 0; + }; -class Translate : public Transform -{ -private: - Vector3 vector; + class Translate : public Transform + { + private: + Vector3 vector; -public: - explicit Translate(Vector3 vector) : vector(vector) {} + public: + explicit Translate(Vector3 vector) : vector(vector) {} - Translate(float x, float y, float z) : vector(x, y, z) {} + Translate(float x, float y, float z) : vector(x, y, z) {} - void apply(float time) noexcept override; -}; + explicit Translate(tinyxml2::XMLElement *transformElement); -class TimedTranslate : public Transform -{ -private: - float time; - bool align; - bool draw; - std::vector points; - std::vector curve{}; - Vector3 yVector{0, 1, 0}; + void apply(float time) noexcept override; + }; -public: - TimedTranslate(float time, bool align, bool draw, std::vector points); + class TimedTranslate : public Transform + { + private: + float time{}; + bool align{}; + bool draw{}; + std::vector points{}; + std::vector curve{}; + Vector3 yVector{0, 1, 0}; - void apply(float gt) noexcept override; + public: + TimedTranslate(float time, bool align, bool draw, std::vector points); -private: - [[nodiscard]] std::pair getGlobalCatmullRomPoint(float gt) const; + explicit TimedTranslate(tinyxml2::XMLElement *transformElement); - void drawCurve() const; + void apply(float gt) noexcept override; -}; + private: + [[nodiscard]] std::pair getGlobalCatmullRomPoint(float gt) const; -class Rotate : public Transform -{ -private: - float angle; - Vector3 vector; + void drawCurve() const; + }; -public: - Rotate(float angle, Vector3 vector) : angle(angle), vector(vector) {} + class Rotate : public Transform + { + private: + float angle{}; + Vector3 vector; - Rotate(float angle, float x, float y, float z) : angle(angle), vector(x, y, z) {} + public: + Rotate(float angle, Vector3 vector) : angle(angle), vector(vector) {} - void apply(float time) noexcept override; -}; + Rotate(float angle, float x, float y, float z) : angle(angle), vector(x, y, z) {} -class TimedRotate : public Transform -{ -private: - float anglePerSecond; - Vector3 vector; + explicit Rotate(tinyxml2::XMLElement *transformElement); -public: - TimedRotate(float time, Vector3 vector); + void apply(float time) noexcept override; + }; - TimedRotate(float time, float x, float y, float z); + class TimedRotate : public Transform + { + private: + float anglePerSecond; + Vector3 vector; - void apply(float gt) noexcept override; -}; + public: + TimedRotate(float time, Vector3 vector); -class Scale : public Transform -{ -private: - float factorX, factorY, factorZ; + TimedRotate(float time, float x, float y, float z); + + explicit TimedRotate(tinyxml2::XMLElement *transformElement); + + void apply(float gt) noexcept override; + }; + + class Scale : public Transform + { + private: + float factorX{}, factorY{}, factorZ{}; + + public: + Scale(float x, float y, float z) : factorX(x), factorY(y), factorZ(z) {} -public: - Scale(float x, float y, float z) : factorX(x), factorY(y), factorZ(z) {} + explicit Scale(tinyxml2::XMLElement *transformElement); - void apply(float time) noexcept override; -}; + void apply(float time) noexcept override; + }; +} #endif //CG2324_TRANSFORM_H diff --git a/include/engine/window.h b/include/engine/window.h index 0a65b13..8ae7e28 100644 --- a/include/engine/window.h +++ b/include/engine/window.h @@ -2,16 +2,20 @@ #define CG2324_WINDOW_H +#include "deps/tinyxml2.h" + class Window { public: - int width, height; - char *title; + int x{}, y{}; + int width{}, height{}; public: Window() = default; - Window(int width, int height, char *title) : width(width), height(height), title(title) {}; + Window(int x, int y, int width, int height) : x(x), y(y), width(width), height(height) {}; + + explicit Window(tinyxml2::XMLElement *windowElement); }; diff --git a/include/engine/world.h b/include/engine/world.h index 3dd4d81..dffbd55 100644 --- a/include/engine/world.h +++ b/include/engine/world.h @@ -2,26 +2,28 @@ #define CG2324_WORLD_H -#include - #include "engine/group.h" #include "engine/camera.h" -#include "engine/window.h" +#include "deps/tinyxml2.h" class World { public: - Window window{}; Camera camera; Group group; public: World() = default; - explicit World(Window window, Camera camera, Group group) : - window(window), camera(std::move(camera)), group(std::move(group)) {} + World(Camera camera, Group group) : camera(std::move(camera)), group(std::move(group)) {} + + explicit World(tinyxml2::XMLElement *worldElement); + + void renderScene(); + + void changeSize(int w, int h) const; - void draw() const; + void processNormalKeys(unsigned char key, int xx, int yy); }; diff --git a/include/engine/xml_world_loader.h b/include/engine/xml_world_loader.h deleted file mode 100644 index 43a0b0a..0000000 --- a/include/engine/xml_world_loader.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef CG2324_XML_WORLD_LOADER_H -#define CG2324_XML_WORLD_LOADER_H - - -#include "engine/world.h" - -namespace XMLWorldLoader -{ - World load(char *filename); -} - - -#endif //CG2324_XML_WORLD_LOADER_H diff --git a/include/generator/box.h b/include/generator/box.h index f397e56..a153b7b 100644 --- a/include/generator/box.h +++ b/include/generator/box.h @@ -2,7 +2,7 @@ #define CG2324_BOX_H -#include "utils/model.h" +#include "generator/model.h" class Box : public Model { diff --git a/include/generator/cone.h b/include/generator/cone.h index 1389a67..be51c3a 100644 --- a/include/generator/cone.h +++ b/include/generator/cone.h @@ -2,7 +2,7 @@ #define CG2324_CONE_H -#include "utils/model.h" +#include "generator/model.h" class Cone : public Model { diff --git a/include/utils/model.h b/include/generator/model.h similarity index 90% rename from include/utils/model.h rename to include/generator/model.h index 97af745..7f19c54 100644 --- a/include/utils/model.h +++ b/include/generator/model.h @@ -5,27 +5,25 @@ #include #include #include -#include "point3.h" -#include "vector3.h" +#include "utils/point3.h" +#include "utils/vector3.h" class Model { protected: std::vector vertices; - unsigned int buffer{}; protected: Model() = default; public: + explicit Model(const std::vector &vertices); explicit Model(const std::vector &models); explicit Model(const std::string &filename); - void initBuffer(); - void draw() const; void writeToFile(const std::string &filename) const; diff --git a/include/generator/patch_model.h b/include/generator/patch_model.h index 0c2b38a..062faba 100644 --- a/include/generator/patch_model.h +++ b/include/generator/patch_model.h @@ -4,7 +4,7 @@ #include #include "utils/point3.h" -#include "utils/model.h" +#include "generator/model.h" class PatchModel : public Model { diff --git a/include/generator/plane.h b/include/generator/plane.h index 08d3c21..3df8edf 100644 --- a/include/generator/plane.h +++ b/include/generator/plane.h @@ -2,7 +2,7 @@ #define CG2324_PLANE_H -#include "utils/model.h" +#include "generator/model.h" class Plane : public Model { diff --git a/include/generator/sphere.h b/include/generator/sphere.h index c595d4a..1bb60da 100644 --- a/include/generator/sphere.h +++ b/include/generator/sphere.h @@ -2,7 +2,7 @@ #define CG2324_SPHERE_H -#include "utils/model.h" +#include "generator/model.h" class Sphere : public Model { diff --git a/include/utils/point3.h b/include/utils/point3.h index c491b4b..4e1e318 100644 --- a/include/utils/point3.h +++ b/include/utils/point3.h @@ -3,20 +3,21 @@ #include +#include "deps/tinyxml2.h" class Vector3; struct Point3 { - float x, y, z; + float x{}, y{}, z{}; Point3() : x(0), y(0), z(0) {} - Point3(float x, float y, float z); + Point3(float x, float y, float z) : x(x), y(y), z(z) {} - Point3 operator+(const Vector3 &vector) const; + explicit Point3(tinyxml2::XMLElement *pointElement); - Point3 operator+(const std::tuple &vector) const; + Point3 operator+(const Vector3 &vector) const; friend std::ostream &operator<<(std::ostream &os, const Point3 &point); diff --git a/include/utils/vector3.h b/include/utils/vector3.h index 6b28801..d11deaf 100644 --- a/include/utils/vector3.h +++ b/include/utils/vector3.h @@ -1,30 +1,29 @@ #ifndef CG2324_VECTOR3_H #define CG2324_VECTOR3_H -#include struct Point3; class Vector3 { public: - float x, y, z; + float x{}, y{}, z{}; public: Vector3() : x(0), y(0), z(0) {} - Vector3(float x, float y, float z); + Vector3(float x, float y, float z) : x(x), y(y), z(z) {} explicit Vector3(const Point3 &p); Vector3(const Point3 &p1, const Point3 &p2); + explicit Vector3(tinyxml2::XMLElement *vectorElement); + Vector3 operator+(const Vector3 &vector) const; Vector3 operator+=(const Vector3 &vector); - Vector3 operator+(const std::tuple &vector) const; - Vector3 operator*(float scalar) const; Vector3 normalize(); diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index c09edaf..166d50b 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -5,11 +5,15 @@ project(engine) add_executable(${PROJECT_NAME} main.cpp group.cpp - transform.cpp - xml_world_loader.cpp world.cpp camera.cpp window.cpp + model.cpp + transform/timed_translate.cpp + transform/translate.cpp + transform/scale.cpp + transform/timed_rotate.cpp + transform/rotate.cpp ) find_package(OpenGL REQUIRED) diff --git a/src/engine/camera.cpp b/src/engine/camera.cpp index cfc77b3..088ecf8 100644 --- a/src/engine/camera.cpp +++ b/src/engine/camera.cpp @@ -1,7 +1,10 @@ #include #include +#include "deps/tinyxml2.h" #include "engine/camera.h" +using namespace tinyxml2; + Camera::Camera(const Point3 &position, const Point3 &lookAt, const Vector3 &up, float fov, float near, float far) : position(position), lookAt(lookAt), up(up), fov(fov), near(near), far(far) { @@ -10,6 +13,32 @@ Camera::Camera(const Point3 &position, const Point3 &lookAt, const Vector3 &up, yaw = atan2f(position.x - lookAt.x, position.z - lookAt.z); } +Camera::Camera(tinyxml2::XMLElement *cameraElement) +{ + XMLElement *positionElement = cameraElement->FirstChildElement("position"); + if (positionElement) { + position = Point3(positionElement); + } + XMLElement *lookAtElement = cameraElement->FirstChildElement("lookAt"); + if (lookAtElement) { + lookAt = Point3(lookAtElement); + } + XMLElement *upElement = cameraElement->FirstChildElement("up"); + if (upElement) { + up = Vector3(upElement); + } + XMLElement *projectionElement = cameraElement->FirstChildElement("projection"); + if (projectionElement) { + projectionElement->QueryFloatAttribute("fov", &fov); + projectionElement->QueryFloatAttribute("near", &near); + projectionElement->QueryFloatAttribute("far", &far); + } + + radius = sqrtf(powf(position.x - lookAt.x, 2) + powf(position.y - lookAt.y, 2) + powf(position.z - lookAt.z, 2)); + pitch = asinf((position.y - lookAt.y) / radius); + yaw = atan2f(position.x - lookAt.x, position.z - lookAt.z); +} + void Camera::place() { glLoadIdentity(); diff --git a/src/engine/group.cpp b/src/engine/group.cpp index 0be23a7..471d561 100644 --- a/src/engine/group.cpp +++ b/src/engine/group.cpp @@ -1,14 +1,59 @@ -#include +#include +#include "deps/tinyxml2.h" +#include "engine/transform.h" #include "engine/group.h" -void Group::initBuffers() +using namespace tinyxml2; +using namespace transform; + +Group::Group(XMLElement *groupElement) { - for (auto &model: this->models) { - model.initBuffer(); + XMLElement *transformsElement = groupElement->FirstChildElement("transform"); + if (transformsElement) { + XMLElement *transformElement = transformsElement->FirstChildElement(); + while (transformElement) { + if (transformElement->Name() == std::string("translate")) { + if (transformElement->Attribute("time")) { + transforms.push_back(std::make_unique(transformElement)); + } else { + transforms.push_back(std::make_unique(transformElement)); + } + } else if (transformElement->Name() == std::string("rotate")) { + if (transformElement->Attribute("time")) { + transforms.push_back(std::make_unique(transformElement)); + } else { + transforms.push_back(std::make_unique(transformElement)); + } + } else if (transformElement->Name() == std::string("scale")) { + transforms.push_back(std::make_unique(transformElement)); + } + transformElement = transformElement->NextSiblingElement(); + } + } + + static std::map modelBuffers; + + XMLElement *modelsElement = groupElement->FirstChildElement("models"); + if (modelsElement) { + XMLElement *modelElement = modelsElement->FirstChildElement("model"); + while (modelElement) { + const char *file = modelElement->Attribute("file"); + if (modelBuffers.find(file) != modelBuffers.end()) { + models.push_back(modelBuffers[file]); + } else { + Model model(file); + modelBuffers[file] = model; + models.push_back(model); + } + models.emplace_back(file); + modelElement = modelElement->NextSiblingElement("model"); + } } - for (auto &group: this->subgroups) { - group.initBuffers(); + XMLElement *subgroupElement = groupElement->FirstChildElement("group"); + while (subgroupElement) { + subgroups.emplace_back(subgroupElement); + subgroupElement = subgroupElement->NextSiblingElement("group"); } } diff --git a/src/engine/main.cpp b/src/engine/main.cpp index 099bd36..4941728 100644 --- a/src/engine/main.cpp +++ b/src/engine/main.cpp @@ -1,21 +1,11 @@ #include #include #include -#include +#include "engine/window.h" #include "engine/world.h" -#include "engine/xml_world_loader.h" +#include "deps/tinyxml2.h" -using std::cos, std::sin, std::sqrt, std::atan2, std::asin; - -void renderScene(); - -void changeSize(int w, int h); - -void processNormalKeys(unsigned char key, int xx, int yy); - -void drawAxis(); - -World world; +using namespace tinyxml2; int main(int argc, char **argv) { @@ -24,115 +14,48 @@ int main(int argc, char **argv) return 1; } - world = XMLWorldLoader::load(argv[1]); + XMLDocument doc; + if (doc.LoadFile(argv[1]) != XML_SUCCESS) { + std::cerr << "Failed to load file." << std::endl; + return 1; + } + + XMLElement *worldElement = doc.FirstChildElement("world"); + if (!worldElement) { + return 1; + } + + XMLElement *windowElement = worldElement->FirstChildElement("window"); + if (!windowElement) { + return 1; + } + + Window window(windowElement); // init GLUT and the window glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); - glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - world.window.width) / 2, - (glutGet(GLUT_SCREEN_HEIGHT) - world.window.height) / 2); - glutInitWindowSize(world.window.width, world.window.height); - glutCreateWindow(world.window.title); - - // required callback registry - glutDisplayFunc(renderScene); - glutReshapeFunc(changeSize); - glutIdleFunc(renderScene); - glutKeyboardFunc(processNormalKeys); + glutInitWindowPosition(window.x, window.y); + glutInitWindowSize(window.width, window.height); + glutCreateWindow(argv[1]); - // init GLEW -#ifndef __APPLE__ glewInit(); -#endif + + static World world(worldElement); + + // callback registry + glutDisplayFunc([]() { world.renderScene(); }); + glutReshapeFunc([](int w, int h) { world.changeSize(w, h); }); + glutIdleFunc([]() { world.renderScene(); }); + glutKeyboardFunc([](unsigned char key, int xx, int yy) { world.processNormalKeys(key, xx, yy); }); // OpenGL settings glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glEnableClientState(GL_VERTEX_ARRAY); - - world.group.initBuffers(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glutMainLoop(); return 1; } - -void renderScene() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - world.camera.place(); - - drawAxis(); - - world.draw(); - - glutSwapBuffers(); -} - -void changeSize(int w, int h) -{ - // Doesn't seem useful. Might be removed. - world.window.width = w; - world.window.height = h; - - // Prevent a divide by zero, when window is too short - // (you can't make a window of zero width). - if (h == 0) - h = 1; - - // Use the Projection Matrix - glMatrixMode(GL_PROJECTION); - - // Reset Matrix - glLoadIdentity(); - - // Set the viewport to be the entire window - glViewport(0, 0, w, h); - - // Set the correct perspective. - world.camera.setPerspective(w, h); - - // Get Back to the ModelView - glMatrixMode(GL_MODELVIEW); -} - -void processNormalKeys(unsigned char key, int xx, int yy) -{ - world.camera.reactKey(key, xx, yy); - - switch (key) { - case 27: // Quit - ESC Key - exit(0); - default: - break; - } - - glutPostRedisplay(); -} - -void drawAxis() -{ - // save current color - float currentColor[4]; - glGetFloatv(GL_CURRENT_COLOR, currentColor); - - glBegin(GL_LINES); - // X Axis in red - glColor3f(1.0f, 0.0f, 0.0f); - glVertex3f(-100.0f, 0.0f, 0.0f); - glVertex3f(100.0f, 0.0f, 0.0f); - // Y Axis in Green - glColor3f(0.0f, 1.0f, 0.0f); - glVertex3f(0.0f, -100.0f, 0.0f); - glVertex3f(0.0f, 100.0f, 0.0f); - // Z Axis in Blue - glColor3f(0.0f, 0.0f, 1.0f); - glVertex3f(0.0f, 0.0f, -100.0f); - glVertex3f(0.0f, 0.0f, 100.0f); - glEnd(); - - // restore previous color - glColor4fv(currentColor); -} diff --git a/src/engine/model.cpp b/src/engine/model.cpp new file mode 100644 index 0000000..446fcc8 --- /dev/null +++ b/src/engine/model.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include "engine/model.h" + +using std::cerr, std::endl, std::ifstream, std::ios, std::string, std::vector; + +Model::Model(const string &filename) +{ + ifstream file(filename, ios::binary); + + if (!file.is_open()) { + cerr << "Error opening file " << filename << endl; + return; + } + + vector vertices; + + float coords[3]; + while (file.read(reinterpret_cast(coords), sizeof(coords))) { + vertices.emplace_back(coords[0], coords[1], coords[2]); + } + + size = (GLsizei) vertices.size(); + + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr) (3 * size * sizeof(float)), vertices.data(), GL_STATIC_DRAW); +} + +void Model::draw() const +{ + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + glDrawArrays(GL_TRIANGLES, 0, size); +} diff --git a/src/engine/transform.cpp b/src/engine/transform.cpp deleted file mode 100644 index a6edd3a..0000000 --- a/src/engine/transform.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include "engine/transform.h" - -std::pair getCatmullRomPoint(float t, Point3 p0, Point3 p1, Point3 p2, Point3 p3); - -std::array buildRotationMatrix(Vector3 x, Vector3 y, Vector3 z); - -TimedTranslate::TimedTranslate(float time, bool align, bool draw, std::vector points) - : time(time), align(align), draw(draw), points(std::move(points)) -{ - float gt = 0.0f; - const unsigned long nSteps = this->points.size() * 100; - const float tStep = time / (float) nSteps; - - for (int i = 0; i < nSteps; i++) { - curve.push_back(getGlobalCatmullRomPoint(gt).first); - gt += tStep; - } -} - -TimedRotate::TimedRotate(float time, Vector3 vector) : vector(vector) -{ - anglePerSecond = (bool) time ? 360.0f / time : 0; -} - -TimedRotate::TimedRotate(float time, float x, float y, float z) : vector(x, y, z) -{ - anglePerSecond = (bool) time ? 360.0f / time : 0; -} - -void Translate::apply(float) noexcept -{ - glTranslatef(this->vector.x, this->vector.y, this->vector.z); -} - -void Rotate::apply(float) noexcept -{ - glRotatef(this->angle, this->vector.x, this->vector.y, this->vector.z); -} - -void Scale::apply(float) noexcept -{ - glScalef(this->factorX, this->factorY, this->factorZ); -} - -void TimedTranslate::apply(float gt) noexcept -{ - if (draw) { - drawCurve(); - } - - auto [pos, dir] = getGlobalCatmullRomPoint(gt); - glTranslatef(pos.x, pos.y, pos.z); - - if (align) { - Vector3 x = Vector3::normalize(dir); // x could be omitted and use dir directly - Vector3 z = Vector3::normalize(Vector3::cross(x, yVector)); - yVector = Vector3::normalize(Vector3::cross(z, x)); - - glMultMatrixf(buildRotationMatrix(x, yVector, z).data()); - } -} - -void TimedRotate::apply(float gt) noexcept -{ - glRotatef(gt * anglePerSecond, vector.x, vector.y, vector.z); -} - -std::pair TimedTranslate::getGlobalCatmullRomPoint(float gt) const -{ - int pointCount = (int) points.size(); - float t = gt / time * (float) pointCount; // this is the real global t - int index = std::floor(t); // which segment - t = t - (float) index; // where within the segment - - // indices store the points - int indices[4]; - indices[0] = (index + pointCount - 1) % pointCount; - indices[1] = (indices[0] + 1) % pointCount; - indices[2] = (indices[1] + 1) % pointCount; - indices[3] = (indices[2] + 1) % pointCount; - - return getCatmullRomPoint(t, points[indices[0]], points[indices[1]], points[indices[2]], points[indices[3]]); -} - -void TimedTranslate::drawCurve() const -{ - glBegin(GL_LINE_LOOP); - for (const auto &point: curve) { - glVertex3f(point.x, point.y, point.z); - } - glEnd(); -} - -std::pair getCatmullRomPoint(float t, Point3 p0, Point3 p1, Point3 p2, Point3 p3) -{ - // catmull-rom matrix - static const float m[4][4]{ - {-0.5f, +1.5f, -1.5f, +0.5f}, - {+1.0f, -2.5f, +2.0f, -0.5f}, - {-0.5f, +0.0f, +0.5f, +0.0f}, - {+0.0f, +1.0f, +0.0f, +0.0f}, - }; - - // point matrix - const float p[4][3]{ - {p0.x, p0.y, p0.z}, - {p1.x, p1.y, p1.z}, - {p2.x, p2.y, p2.z}, - {p3.x, p3.y, p3.z}, - }; - - const float tv[4] = {powf(t, 3), powf(t, 2), t, 1}; - const float tvd[4] = {3 * powf(t, 2), 2 * t, 1, 0}; - - float pv[3] = {0}; - float dv[3] = {0}; - - for (int i = 0; i < 3; i++) { - float a[4] = {0}; - for (int j = 0; j < 4; j++) { - for (int k = 0; k < 4; k++) { - a[j] += m[j][k] * p[k][i]; - } - } - for (int j = 0; j < 4; j++) { - pv[i] += tv[j] * a[j]; - dv[i] += tvd[j] * a[j]; - } - } - - return {{pv[0], pv[1], pv[2]}, - {dv[0], dv[1], dv[2]}}; -} - -std::array buildRotationMatrix(Vector3 x, Vector3 y, Vector3 z) -{ - return { - x.x, x.y, x.z, 0, - y.x, y.y, y.z, 0, - z.x, z.y, z.z, 0, - 0, 0, 0, 1, - }; -} diff --git a/src/engine/transform/rotate.cpp b/src/engine/transform/rotate.cpp new file mode 100644 index 0000000..b90b063 --- /dev/null +++ b/src/engine/transform/rotate.cpp @@ -0,0 +1,19 @@ +#include +#include "deps/tinyxml2.h" +#include "engine/transform.h" + +using namespace tinyxml2; + +namespace transform +{ + Rotate::Rotate(XMLElement *transformElement) + { + transformElement->QueryFloatAttribute("angle", &angle); + vector = Vector3(transformElement); + } + + void Rotate::apply(float) noexcept + { + glRotatef(this->angle, this->vector.x, this->vector.y, this->vector.z); + } +} diff --git a/src/engine/transform/scale.cpp b/src/engine/transform/scale.cpp new file mode 100644 index 0000000..c9e2228 --- /dev/null +++ b/src/engine/transform/scale.cpp @@ -0,0 +1,20 @@ +#include +#include "deps/tinyxml2.h" +#include "engine/transform.h" + +using namespace tinyxml2; + +namespace transform +{ + Scale::Scale(XMLElement *transformElement) + { + transformElement->QueryFloatAttribute("x", &factorX); + transformElement->QueryFloatAttribute("y", &factorY); + transformElement->QueryFloatAttribute("z", &factorZ); + } + + void Scale::apply(float) noexcept + { + glScalef(this->factorX, this->factorY, this->factorZ); + } +} diff --git a/src/engine/transform/timed_rotate.cpp b/src/engine/transform/timed_rotate.cpp new file mode 100644 index 0000000..54de71b --- /dev/null +++ b/src/engine/transform/timed_rotate.cpp @@ -0,0 +1,31 @@ +#include +#include "deps/tinyxml2.h" +#include "engine/transform.h" + +using namespace tinyxml2; + +namespace transform +{ + TimedRotate::TimedRotate(XMLElement *transformElement) + { + float time; + transformElement->QueryFloatAttribute("time", &time); + anglePerSecond = (bool) time ? 360.0f / time : 0; + vector = Vector3(transformElement); + } + + TimedRotate::TimedRotate(float time, Vector3 vector) : vector(vector) + { + anglePerSecond = (bool) time ? 360.0f / time : 0; + } + + TimedRotate::TimedRotate(float time, float x, float y, float z) : vector(x, y, z) + { + anglePerSecond = (bool) time ? 360.0f / time : 0; + } + + void TimedRotate::apply(float gt) noexcept + { + glRotatef(gt * anglePerSecond, vector.x, vector.y, vector.z); + } +} diff --git a/src/engine/transform/timed_translate.cpp b/src/engine/transform/timed_translate.cpp new file mode 100644 index 0000000..462a2b2 --- /dev/null +++ b/src/engine/transform/timed_translate.cpp @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include "deps/tinyxml2.h" +#include "engine/transform.h" + +using namespace tinyxml2; + +std::pair getCatmullRomPoint(float t, Point3 p0, Point3 p1, Point3 p2, Point3 p3); + +std::array buildRotationMatrix(Vector3 x, Vector3 y, Vector3 z); + +namespace transform +{ + TimedTranslate::TimedTranslate(float time, bool align, bool draw, std::vector points) + : time(time), align(align), draw(draw), points(std::move(points)) + { + float gt = 0.0f; + const unsigned long nSteps = this->points.size() * 100; + const float tStep = time / (float) nSteps; + + for (int i = 0; i < nSteps; i++) { + curve.push_back(getGlobalCatmullRomPoint(gt).first); + gt += tStep; + } + } + + TimedTranslate::TimedTranslate(XMLElement *transformElement) + { + transformElement->QueryFloatAttribute("time", &time); + transformElement->QueryBoolAttribute("align", &align); + transformElement->QueryBoolAttribute("draw", &draw); + + XMLElement *pointElement = transformElement->FirstChildElement("point"); + while (pointElement) { + points.emplace_back(pointElement); + pointElement = pointElement->NextSiblingElement("point"); + } + + float gt = 0.0f; + const unsigned long nSteps = this->points.size() * 100; + const float tStep = time / (float) nSteps; + + for (int i = 0; i < nSteps; i++) { + curve.push_back(getGlobalCatmullRomPoint(gt).first); + gt += tStep; + } + } + + void TimedTranslate::apply(float gt) noexcept + { + if (draw) { + drawCurve(); + } + + auto [pos, dir] = getGlobalCatmullRomPoint(gt); + glTranslatef(pos.x, pos.y, pos.z); + + if (align) { + Vector3 x = Vector3::normalize(dir); // x could be omitted and use dir directly + Vector3 z = Vector3::normalize(Vector3::cross(x, yVector)); + yVector = Vector3::normalize(Vector3::cross(z, x)); + + glMultMatrixf(buildRotationMatrix(x, yVector, z).data()); + } + } + + void TimedTranslate::drawCurve() const + { + glBegin(GL_LINE_LOOP); + for (const auto &point: curve) { + glVertex3f(point.x, point.y, point.z); + } + glEnd(); + } + + std::pair TimedTranslate::getGlobalCatmullRomPoint(float gt) const + { + int pointCount = (int) points.size(); + float t = gt / time * (float) pointCount; // this is the real global t + int index = std::floor(t); // which segment + t = t - (float) index; // where within the segment + + // indices store the points + int indices[4]; + indices[0] = (index + pointCount - 1) % pointCount; + indices[1] = (indices[0] + 1) % pointCount; + indices[2] = (indices[1] + 1) % pointCount; + indices[3] = (indices[2] + 1) % pointCount; + + return getCatmullRomPoint(t, points[indices[0]], points[indices[1]], points[indices[2]], points[indices[3]]); + } +} + +std::pair getCatmullRomPoint(float t, Point3 p0, Point3 p1, Point3 p2, Point3 p3) +{ + // catmull-rom matrix + static const float m[4][4]{ + {-0.5f, +1.5f, -1.5f, +0.5f}, + {+1.0f, -2.5f, +2.0f, -0.5f}, + {-0.5f, +0.0f, +0.5f, +0.0f}, + {+0.0f, +1.0f, +0.0f, +0.0f}, + }; + + // point matrix + const float p[4][3]{ + {p0.x, p0.y, p0.z}, + {p1.x, p1.y, p1.z}, + {p2.x, p2.y, p2.z}, + {p3.x, p3.y, p3.z}, + }; + + const float tv[4] = {powf(t, 3), powf(t, 2), t, 1}; + const float tvd[4] = {3 * powf(t, 2), 2 * t, 1, 0}; + + float pv[3] = {0}; + float dv[3] = {0}; + + for (int i = 0; i < 3; i++) { + float a[4] = {0}; + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 4; k++) { + a[j] += m[j][k] * p[k][i]; + } + } + for (int j = 0; j < 4; j++) { + pv[i] += tv[j] * a[j]; + dv[i] += tvd[j] * a[j]; + } + } + + return {{pv[0], pv[1], pv[2]}, + {dv[0], dv[1], dv[2]}}; +} + +std::array buildRotationMatrix(Vector3 x, Vector3 y, Vector3 z) +{ + return { + x.x, x.y, x.z, 0, + y.x, y.y, y.z, 0, + z.x, z.y, z.z, 0, + 0, 0, 0, 1, + }; +} diff --git a/src/engine/transform/translate.cpp b/src/engine/transform/translate.cpp new file mode 100644 index 0000000..228b9bd --- /dev/null +++ b/src/engine/transform/translate.cpp @@ -0,0 +1,18 @@ +#include +#include "deps/tinyxml2.h" +#include "engine/transform.h" + +using namespace tinyxml2; + +namespace transform +{ + Translate::Translate(XMLElement *transformElement) + { + vector = Vector3(transformElement); + } + + void Translate::apply(float) noexcept + { + glTranslatef(this->vector.x, this->vector.y, this->vector.z); + } +} diff --git a/src/engine/window.cpp b/src/engine/window.cpp index 2eca412..27a53bc 100644 --- a/src/engine/window.cpp +++ b/src/engine/window.cpp @@ -1 +1,12 @@ +#include "deps/tinyxml2.h" #include "engine/window.h" + +using namespace tinyxml2; + +Window::Window(XMLElement *windowElement) +{ + windowElement->QueryIntAttribute("x", &x); + windowElement->QueryIntAttribute("y", &y); + windowElement->QueryIntAttribute("width", &width); + windowElement->QueryIntAttribute("height", &height); +} diff --git a/src/engine/world.cpp b/src/engine/world.cpp index 53fe367..56992dc 100644 --- a/src/engine/world.cpp +++ b/src/engine/world.cpp @@ -1,7 +1,95 @@ -#include +#include +#include +#include "deps/tinyxml2.h" #include "engine/world.h" -void World::draw() const +using namespace tinyxml2; + +void drawAxis(); + +World::World(XMLElement *worldElement) { + XMLElement *cameraElement = worldElement->FirstChildElement("camera"); + if (cameraElement) { + camera = Camera(cameraElement); + } + XMLElement *groupElement = worldElement->FirstChildElement("group"); + if (groupElement) { + group = Group(groupElement); + } +} + +void World::renderScene() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + camera.place(); + + drawAxis(); + group.draw((float) glutGet(GLUT_ELAPSED_TIME) / 1000.0f); + + glutSwapBuffers(); +} + +void World::changeSize(int w, int h) const +{ + // Prevent a divide by zero, when window is too short + // (you can't make a window of zero width). + if (h == 0) + h = 1; + + // Use the Projection Matrix + glMatrixMode(GL_PROJECTION); + + // Reset Matrix + glLoadIdentity(); + + // Set the viewport to be the entire window + glViewport(0, 0, w, h); + + // Set the correct perspective. + camera.setPerspective(w, h); + + // Get Back to the ModelView + glMatrixMode(GL_MODELVIEW); +} + +void World::processNormalKeys(unsigned char key, int xx, int yy) +{ + camera.reactKey(key, xx, yy); + + switch (key) { + case 27: // Quit - ESC Key + exit(0); + default: + break; + } + + glutPostRedisplay(); +} + +void drawAxis() +{ + // save current color + float currentColor[4]; + glGetFloatv(GL_CURRENT_COLOR, currentColor); + + glBegin(GL_LINES); + // X Axis in red + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(-100.0f, 0.0f, 0.0f); + glVertex3f(100.0f, 0.0f, 0.0f); + // Y Axis in Green + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, -100.0f, 0.0f); + glVertex3f(0.0f, 100.0f, 0.0f); + // Z Axis in Blue + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, -100.0f); + glVertex3f(0.0f, 0.0f, 100.0f); + glEnd(); + + // restore previous color + glColor4fv(currentColor); } diff --git a/src/engine/xml_world_loader.cpp b/src/engine/xml_world_loader.cpp deleted file mode 100644 index ec503af..0000000 --- a/src/engine/xml_world_loader.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include -#include "deps/tinyxml2.h" -#include "engine/world.h" - -using namespace tinyxml2; - -namespace XMLWorldLoader -{ - Group parseGroup(XMLElement *groupElement); - - // World load(const std::string &filename) - World load(char *filename) - { - int width{}, height{}; - float positionX{}, positionY{}, positionZ{}; - float lookAtX{}, lookAtY{}, lookAtZ{}; - float upX{}, upY{}, upZ{}; - float fov{}, near{}, far{}; - Group topGroup; - - XMLDocument doc; - if (doc.LoadFile(filename) == XML_SUCCESS) { - XMLElement *worldElement = doc.FirstChildElement("world"); - if (worldElement) { - XMLElement *windowElement = worldElement->FirstChildElement("window"); - if (windowElement) { - windowElement->QueryIntAttribute("width", &width); - windowElement->QueryIntAttribute("height", &height); - } - - XMLElement *cameraElement = worldElement->FirstChildElement("camera"); - if (cameraElement) { - XMLElement *positionElement = cameraElement->FirstChildElement("position"); - if (positionElement) { - positionElement->QueryFloatAttribute("x", &positionX); - positionElement->QueryFloatAttribute("y", &positionY); - positionElement->QueryFloatAttribute("z", &positionZ); - } - XMLElement *lookAtElement = cameraElement->FirstChildElement("lookAt"); - if (lookAtElement) { - lookAtElement->QueryFloatAttribute("x", &lookAtX); - lookAtElement->QueryFloatAttribute("y", &lookAtY); - lookAtElement->QueryFloatAttribute("z", &lookAtZ); - } - XMLElement *upElement = cameraElement->FirstChildElement("up"); - if (upElement) { - upElement->QueryFloatAttribute("x", &upX); - upElement->QueryFloatAttribute("y", &upY); - upElement->QueryFloatAttribute("z", &upZ); - } - XMLElement *projectionElement = cameraElement->FirstChildElement("projection"); - if (projectionElement) { - projectionElement->QueryFloatAttribute("fov", &fov); - projectionElement->QueryFloatAttribute("near", &near); - projectionElement->QueryFloatAttribute("far", &far); - } - } - - XMLElement *groupElement = worldElement->FirstChildElement("group"); - if (groupElement) { - topGroup = parseGroup(groupElement); - } - } - } else { - std::cerr << "Failed to load file." << std::endl; - } - - Window window(width, height, filename); - Camera camera({positionX, positionY, positionZ}, {lookAtX, lookAtY, lookAtZ}, {upX, upY, upZ}, fov, near, far); - - return World(window, camera, std::move(topGroup)); - } - - Group parseGroup(XMLElement *groupElement) - { - std::vector> transforms; - std::vector models; - std::vector subgroups; - - XMLElement *transformsElement = groupElement->FirstChildElement("transform"); - if (transformsElement) { - XMLElement *transformElement = transformsElement->FirstChildElement(); - while (transformElement) { - if (transformElement->Name() == std::string("translate")) { - if (transformElement->Attribute("time")) { - float time; - bool align, draw; - std::vector points; - transformElement->QueryFloatAttribute("time", &time); - transformElement->QueryBoolAttribute("align", &align); - transformElement->QueryBoolAttribute("draw", &draw); - XMLElement *pointElement = transformElement->FirstChildElement("point"); - while (pointElement) { - float x, y, z; - pointElement->QueryFloatAttribute("x", &x); - pointElement->QueryFloatAttribute("y", &y); - pointElement->QueryFloatAttribute("z", &z); - points.emplace_back(x, y, z); - pointElement = pointElement->NextSiblingElement("point"); - } - transforms.push_back(std::make_unique(time, align, draw, std::move(points))); - } else { - float x, y, z; - transformElement->QueryFloatAttribute("x", &x); - transformElement->QueryFloatAttribute("y", &y); - transformElement->QueryFloatAttribute("z", &z); - transforms.push_back(std::make_unique(x, y, z)); - } - } else if (transformElement->Name() == std::string("rotate")) { - float x, y, z; - transformElement->QueryFloatAttribute("x", &x); - transformElement->QueryFloatAttribute("y", &y); - transformElement->QueryFloatAttribute("z", &z); - if (transformElement->Attribute("time")) { - float time; - transformElement->QueryFloatAttribute("time", &time); - transforms.push_back(std::make_unique(time, x, y, z)); - } else { - float angle; - transformElement->QueryFloatAttribute("angle", &angle); - transforms.push_back(std::make_unique(angle, x, y, z)); - } - } else if (transformElement->Name() == std::string("scale")) { - float x, y, z; - transformElement->QueryFloatAttribute("x", &x); - transformElement->QueryFloatAttribute("y", &y); - transformElement->QueryFloatAttribute("z", &z); - transforms.push_back(std::make_unique(x, y, z)); - } - transformElement = transformElement->NextSiblingElement(); - } - } - - XMLElement *modelsElement = groupElement->FirstChildElement("models"); - if (modelsElement) { - XMLElement *modelElement = modelsElement->FirstChildElement("model"); - while (modelElement) { - const char *file = modelElement->Attribute("file"); - models.emplace_back(file); - modelElement = modelElement->NextSiblingElement("model"); - } - } - - XMLElement *subgroupElement = groupElement->FirstChildElement("group"); - while (subgroupElement) { - subgroups.push_back(parseGroup(subgroupElement)); - subgroupElement = subgroupElement->NextSiblingElement("group"); - } - - return {std::move(transforms), std::move(models), std::move(subgroups)}; - } -} diff --git a/src/generator/CMakeLists.txt b/src/generator/CMakeLists.txt index 2214b09..9930ea3 100644 --- a/src/generator/CMakeLists.txt +++ b/src/generator/CMakeLists.txt @@ -9,26 +9,10 @@ add_executable(${PROJECT_NAME} sphere.cpp cone.cpp patch_model.cpp + model.cpp ) -find_package(OpenGL REQUIRED) -include_directories(${OpenGL_INCLUDE_DIRS}) -link_directories(${OpenGL_LIBRARY_DIRS}) -add_definitions(${OpenGL_DEFINITIONS}) - -find_package(GLUT REQUIRED) -include_directories(${GLUT_INCLUDE_DIRS}) -link_directories(${GLUT_LIBRARY_DIRS}) -add_definitions(${GLUT_DEFINITIONS}) - -find_package(GLEW REQUIRED) -include_directories(${GLEW_INCLUDE_DIRS}) -link_libraries(${GLEW_LIBRARIES}) - target_link_libraries( ${PROJECT_NAME} utils - ${OPENGL_LIBRARIES} - ${GLUT_LIBRARIES} - ${GLEW_LIBRARIES} ) diff --git a/src/utils/model.cpp b/src/generator/model.cpp similarity index 80% rename from src/utils/model.cpp rename to src/generator/model.cpp index 33875e8..21a2c55 100644 --- a/src/utils/model.cpp +++ b/src/generator/model.cpp @@ -1,10 +1,9 @@ #include #include -#include #include #include #include "utils/vector3.h" -#include "utils/model.h" +#include "generator/model.h" using std::string, std::cerr, std::endl, std::move, std::cos, std::sin, @@ -22,11 +21,9 @@ Model::Model(const string &filename) return; } - float x, y, z; - while (file.read(reinterpret_cast(&x), sizeof(x)) && - file.read(reinterpret_cast(&y), sizeof(y)) && - file.read(reinterpret_cast(&z), sizeof(z))) { - vertices.emplace_back(x, y, z); + float coords[3]; + while (file.read(reinterpret_cast(coords), sizeof(coords))) { + vertices.emplace_back(coords[0], coords[1], coords[2]); } } @@ -39,20 +36,6 @@ Model::Model(const vector &models) } } -void Model::initBuffer() -{ - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr) (3 * vertices.size() * sizeof(float)), vertices.data(), GL_STATIC_DRAW); -} - -void Model::draw() const -{ - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glVertexPointer(3, GL_FLOAT, 0, nullptr); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei) vertices.size()); -} - void Model::translate(const Vector3 &v) { vector translatedVertices; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 64d38af..9aefa52 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.27) add_library(utils - model.cpp point3.cpp vector3.cpp ) diff --git a/src/utils/point3.cpp b/src/utils/point3.cpp index fecaa8a..5bb57ba 100644 --- a/src/utils/point3.cpp +++ b/src/utils/point3.cpp @@ -1,20 +1,21 @@ #include +#include "deps/tinyxml2.h" #include "utils/vector3.h" #include "utils/point3.h" -using std::ostream, std::tuple; +using std::ostream; +using namespace tinyxml2; -Point3::Point3(float x, float y, float z) : x(x), y(y), z(z) {} - -Point3 Point3::operator+(const Vector3 &v) const +Point3::Point3(XMLElement *vectorElement) { - return {x + v.x, y + v.y, z + v.z}; + vectorElement->QueryFloatAttribute("x", &x); + vectorElement->QueryFloatAttribute("y", &y); + vectorElement->QueryFloatAttribute("z", &z); } -Point3 Point3::operator+(const tuple &v) const +Point3 Point3::operator+(const Vector3 &v) const { - auto [vx, vy, vz] = v; - return {x + vx, y + vy, z + vz}; + return {x + v.x, y + v.y, z + v.z}; } ostream &operator<<(ostream &os, const Point3 &point) diff --git a/src/utils/vector3.cpp b/src/utils/vector3.cpp index 9c69f2f..c51d74e 100644 --- a/src/utils/vector3.cpp +++ b/src/utils/vector3.cpp @@ -1,15 +1,22 @@ -#include #include +#include "deps/tinyxml2.h" #include "utils/vector3.h" #include "utils/point3.h" using std::tuple; - -Vector3::Vector3(float x, float y, float z) : x(x), y(y), z(z) {} +using namespace tinyxml2; Vector3::Vector3(const Point3 &p) : x(p.x), y(p.y), z(p.z) {} -Vector3::Vector3(const Point3 &p1, const Point3 &p2) : x(p2.x - p1.x), y(p2.y - p1.y), z(p2.z - p1.z) {} +Vector3::Vector3(const Point3 &p1, const Point3 &p2) + : x(p2.x - p1.x), y(p2.y - p1.y), z(p2.z - p1.z) {} + +Vector3::Vector3(XMLElement *vectorElement) +{ + vectorElement->QueryFloatAttribute("x", &x); + vectorElement->QueryFloatAttribute("y", &y); + vectorElement->QueryFloatAttribute("z", &z); +} Vector3 Vector3::operator+(const Vector3 &v) const { @@ -24,12 +31,6 @@ Vector3 Vector3::operator+=(const Vector3 &vector) return *this; } -Vector3 Vector3::operator+(const tuple &v) const -{ - auto [vx, vy, vz] = v; - return {x + vx, y + vy, z + vz}; -} - Vector3 Vector3::operator*(float scalar) const { return {x * scalar, y * scalar, z * scalar};