diff --git a/.gitignore b/.gitignore index 6d58447..a88a25e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -ExternalResources \ No newline at end of file +ExternalResources + +Abgabe + +963830_rohmenf.zip + +bugs.md \ No newline at end of file diff --git a/RubiksCube/Camera.h b/RubiksCube/Camera.h new file mode 100644 index 0000000..3dbe723 --- /dev/null +++ b/RubiksCube/Camera.h @@ -0,0 +1,112 @@ +#pragma once + +#include + +#include + + + +#include "Entity.h" + +#include + +class Camera : public Entity { +private: + GLFWwindow* _window; + InputSystem* _inputSystem; + + glm::mat4 _view; + glm::mat4 _projection; + + glm::quat _orientation; + + glm::vec2 _dragStart; + bool _wasMouseClicked; + + float _scrollPositionPrevious; + float _scrollPositionDelta; + + static inline float cameraDistance = 8.15f; + + static inline Camera* _instance; + +public: + const glm::mat4& View() const { return _view * glm::mat4_cast(_orientation); } + const glm::mat4& Projection() const { return _projection; } + +public: + Camera(GLFWwindow* window, InputSystem* inputSystem) : Entity(nullptr) { + _instance = this; + _window = window; + _inputSystem = inputSystem; + + _wasMouseClicked = false; + + _view = glm::lookAt(glm::vec3(0.0f, 0.0f, cameraDistance), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + _projection = glm::mat4(1.0f); + } + + void Update(double deltaTime) override { + int screenWidth; + int screenHeight; + + glfwGetFramebufferSize(_window, &screenWidth, &screenHeight); + + float aspec = (float)screenWidth / (float)screenHeight; + + _projection = glm::perspective(glm::radians(45.0f), aspec, 0.1f, 100.0f); + + if (_inputSystem->WasKeyPressed(GLFW_KEY_SPACE)) + _orientation = glm::quat(1.0f, glm::vec3(0.0f, 0.0f, 0.0f)); + + glm::fvec2 velocity(0.0f, 0.0f); + if (_inputSystem->IsKeyPressed(GLFW_KEY_UP)) + velocity.x = glm::radians(90.0f); + if (_inputSystem->IsKeyPressed(GLFW_KEY_DOWN)) + velocity.x = glm::radians(-90.0f); + + if (_inputSystem->IsKeyPressed(GLFW_KEY_RIGHT)) + velocity.y = glm::radians(90.0f); + if (_inputSystem->IsKeyPressed(GLFW_KEY_LEFT)) + velocity.y = glm::radians(-90.0f); + + if (!_wasMouseClicked && _inputSystem->IsRightMouseDown()) { + _inputSystem->GetMousePos(_dragStart); + } + + if (_wasMouseClicked && _inputSystem->IsRightMouseDown()) { + glm::vec2 currentMousePositions; + _inputSystem->GetMousePos(currentMousePositions); + glm::vec2 dragDiff = currentMousePositions - _dragStart; + _dragStart = currentMousePositions; + + _orientation = glm::quat(1.0f, glm::vec3(dragDiff.y, dragDiff.x, 0.0f) * 0.008f) * _orientation; + _orientation = glm::normalize(_orientation); + } + + _wasMouseClicked = _inputSystem->IsRightMouseDown(); + + glm::quat velocityQuaternion = glm::quat(0.0f, glm::vec3(velocity.x, velocity.y, 0.0f)); + + _orientation += 0.5f * (float)deltaTime * velocityQuaternion * _orientation; + _orientation = glm::normalize(_orientation); + + _view = glm::translate(_view, glm::vec3(0.0f, 0.0f, _scrollPositionDelta)); + _scrollPositionDelta = 0.0f; + + // since callbacks need to be static use a singleton + glfwSetScrollCallback(_window, Camera::scrollCallbackGlobal); + } + + static void scrollCallbackGlobal(GLFWwindow* window, double xOffset, double yOffset) { + _instance->scrollCallback(window, xOffset, yOffset); + } + + void scrollCallback(GLFWwindow* window, double xOffset, double yOffset) { + _scrollPositionDelta = yOffset; + } + + void SetAspectRatio(float aspec) { + _projection = glm::perspective(glm::radians(45.0f), aspec, 0.1f, 100.0f); + } +}; diff --git a/RubiksCube/Cube.cpp b/RubiksCube/Cube.cpp index d5d3e33..12b498e 100644 --- a/RubiksCube/Cube.cpp +++ b/RubiksCube/Cube.cpp @@ -1,9 +1,7 @@ #include "Cube.h" -Cube::Cube() +Cube::Cube() : Entity(nullptr) { - this->_transform = glm::mat4(1.0f); - for(int x=-1;x<=1;x++) { for(int y=-1;y<=1;y++) { for(int z=-1;z<=1;z++) { @@ -35,7 +33,7 @@ Cube::Cube() sideColors[Side::Forward] = SideColor[Side::Forward]; } - PartitionedCube* cubeAtIndex = new PartitionedCube(glm::vec3(x, y, z), sideColors); + PartitionedCube* cubeAtIndex = new PartitionedCube(this, glm::vec3(x, y, z), sideColors); this->_children[x + 1][y + 1][z + 1] = cubeAtIndex; } @@ -53,20 +51,6 @@ Cube::~Cube() { } } -void Cube::Update(double deltaTime) { - for(int x=0;x<3;x++) { - for(int y=0;y<3;y++) { - for(int z=0;z<3;z++) { - _children[x][y][z]->Update(deltaTime); - } - } - } -} - -void Cube::Transform(glm::mat4 transform) { - this->_transform *= transform; -} - void Cube::_FindAxisChildren(const glm::ivec3& axis, int index, std::vector& result) const { glm::ivec3 orientationBuffer[3] = { axis, @@ -83,12 +67,20 @@ void Cube::_FindAxisChildren(const glm::ivec3& axis, int index, std::vector_children, sizeof(this->_children)); std::vector result; + glm::mat3 transform = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f) * turns, glm::vec3(axis)); + + for(int x=0;x<3;x++) { + for(int y=0;y<3;y++) { + transform[x][y] = lroundf(transform[x][y]); + } + } + _FindAxisChildren(axis, index, result); for(auto& position : result) { @@ -98,20 +90,22 @@ void Cube::_TransformData(const glm::ivec3& axis, int index, const glm::mat3& tr newPosition += glm::ivec3(1); _children[newPosition.x][newPosition.y][newPosition.z] = previousCubeList[position.x][position.y][position.z]; + + _children[newPosition.x][newPosition.y][newPosition.z]->TransformData(axis, turns); } } void Cube::Transform(const glm::ivec3& axis, int index, const glm::mat3& transform) { - _TransformData(axis, index, transform); + // _TransformData(axis, index, transform); std::vector result; - _FindAxisChildren(transform * axis, index, result); + _FindAxisChildren(axis, index, result); for(auto& position : result) { PartitionedCube* cube = _children[position.x + 1][position.y + 1][position.z + 1]; - cube->Transform(glm::mat4(transform)); + cube->TransformLocal(glm::mat4(transform)); } } @@ -123,16 +117,29 @@ void Cube::TransformTemp(const glm::ivec3& axis, int index, const glm::mat3& tra for(auto& position : result) { PartitionedCube* cube = _children[position.x + 1][position.y + 1][position.z + 1]; - cube->Transform(glm::mat4(transform)); + cube->TransformTemp(glm::mat4(transform)); } } -void Cube::TransformAnimation(const glm::ivec3& axis, int index, const glm::mat3& transform, float duration) { - _TransformData(axis, index, transform); +void Cube::UndoTransformTemp() +{ + for(int x=0;x<3;x++) { + for(int y=0;y<3;y++) { + for(int z=0;z<3;z++) { + _children[x][y][z]->UndoTransformTemp(); + } + } + } +} + +void Cube::TransformAnimation(const glm::ivec3& axis, int index, float angle, float duration) { + // _TransformData(axis, index, transform); std::vector result; - _FindAxisChildren(transform * axis, index, result); + _FindAxisChildren(axis, index, result); + + glm::quat transform = glm::angleAxis(angle, glm::vec3(axis)); for(auto& position : result) { PartitionedCube* cube = _children[position.x + 1][position.y + 1][position.z + 1]; diff --git a/RubiksCube/Cube.h b/RubiksCube/Cube.h index 0774247..4c615fd 100644 --- a/RubiksCube/Cube.h +++ b/RubiksCube/Cube.h @@ -1,36 +1,33 @@ #pragma once -#include +#include + +#include + +#include "Entity.h" #include "PartitionedCube.h" // Colors according to https://ruwix.com/the-rubiks-cube/japanese-western-color-schemes/ -class Cube +class Cube : public Entity { private: - glm::mat4 _transform; - PartitionedCube* _children[3][3][3]; - void _TransformData(const glm::ivec3& axis, int index, const glm::mat3& transform); - void _FindAxisChildren(const glm::ivec3& axis, int index, std::vector& result) const; public: Cube(); - ~Cube(); - - void Update(double deltaTime); + virtual ~Cube(); const int ChildPartitionsCount = 27; PartitionedCube** Children() { return &(_children[0][0][0]); } - const glm::mat4& Transform() const { return _transform; } - - void TransformAnimation(const glm::ivec3& axis, int index, const glm::mat3& transform, float duration); + void TransformData(const glm::ivec3& axis, int index, int turns); + void TransformAnimation(const glm::ivec3& axis, int index, float angle, float duration); void Transform(const glm::ivec3& axis, int index, const glm::mat3& transform); void TransformTemp(const glm::ivec3& axis, int index, const glm::mat3& transform); - void Transform(glm::mat4 transform); + void UndoTransformTemp(); static constexpr Color SideColor[6] = { Color::ORANGE(), diff --git a/RubiksCube/Debug.cpp b/RubiksCube/Debug.cpp new file mode 100644 index 0000000..4005cb1 --- /dev/null +++ b/RubiksCube/Debug.cpp @@ -0,0 +1,68 @@ +#include "Debug.h" + +#include + +#include + +void Debug::Render(DefaultUniform& uniform) +{ + RenderLines(uniform); + + _lines.clear(); +} + +void Debug::RenderLines(DefaultUniform& uniform) const +{ + glUseProgram(_shader); + glBindVertexArray(_vba); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + + int vertexCount = _lines.size() * 6; + + int viewportWidth, viewportHeight; + glfwGetFramebufferSize(_window, &viewportWidth, &viewportHeight); + + glm::vec2 viewport = glm::vec2(viewportWidth, viewportHeight); + + + + glUniform2fv(_uniformViewportRes, 1, glm::value_ptr(viewport)); + glUniformMatrix4fv(_uniformModel, 1, GL_FALSE, glm::value_ptr(uniform.model)); + glUniformMatrix4fv(_uniformView, 1, GL_FALSE, glm::value_ptr(uniform.view)); + glUniformMatrix4fv(_uniformProjection, 1, GL_FALSE, glm::value_ptr(uniform.projection)); + + + + DebugVertexData* data = new DebugVertexData[vertexCount]; + int index = 0; + for(const auto& line : _lines) { + int vertexIndex = index * 6; + + glm::vec3 quad[4] = { + line.positionFirst, + line.positionFirst, + line.positionLast, + line.positionLast + }; + + glm::vec3 direction = line.positionLast - line.positionFirst; + + data[ vertexIndex + 0 ] = DebugVertexData { quad[0], direction, line.color, line.lineWidth }; + data[ vertexIndex + 1 ] = DebugVertexData { quad[1], direction, line.color, line.lineWidth }; + data[ vertexIndex + 2 ] = DebugVertexData { quad[2], direction, line.color, line.lineWidth }; + data[ vertexIndex + 3 ] = DebugVertexData { quad[1], direction, line.color, line.lineWidth }; + data[ vertexIndex + 4 ] = DebugVertexData { quad[2], direction, line.color, line.lineWidth }; + data[ vertexIndex + 5 ] = DebugVertexData { quad[3], direction, line.color, line.lineWidth }; + + index++; + } + + glBufferData(GL_ARRAY_BUFFER, sizeof(DebugVertexData) * vertexCount, data, GL_STATIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, vertexCount); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glUseProgram(0); + + delete[] data; +} diff --git a/RubiksCube/Debug.h b/RubiksCube/Debug.h new file mode 100644 index 0000000..8d33598 --- /dev/null +++ b/RubiksCube/Debug.h @@ -0,0 +1,146 @@ +#pragma once + +#include "Color.h" + +#include "ShaderUtil.h" + +#include "Mesh.h" + +#include "Entity.h" + +#include + +#include +#include + +#include + +struct GLFWwindow; + +struct DebugLine { + glm::vec3 positionFirst; + glm::vec3 positionLast; + float lineWidth; + Color color; +}; + +struct DebugPoint { + glm::vec3 position; + Color color; +}; + +struct DebugDirection { + glm::vec3 origin; + glm::vec3 direction; + float lineWidth; + Color color; +}; + +struct DebugVertexData { + glm::vec3 v; + glm::vec3 tangent; + Color color; + float lineWidth; +}; + +class Debug : public Entity +{ +private: + std::vector _lines; + std::vector _points; + + GLFWwindow* _window; + + GLuint _vba; + GLuint _vbo; + GLuint _shader; + + GLuint _uniformViewportRes; + GLuint _uniformModel; + GLuint _uniformView; + GLuint _uniformProjection; + +public: + Debug() : Entity(nullptr) { } + Debug(GLFWwindow* window) : Entity(nullptr) { + _window = window; + + glGenBuffers(1, &_vbo); + glGenVertexArrays(1, &_vba); + glBindVertexArray(_vba); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(DebugVertexData), (void*)offsetof(DebugVertexData, v)); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(DebugVertexData), (void*)offsetof(DebugVertexData, tangent)); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(DebugVertexData), (void*)offsetof(DebugVertexData, color)); + glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(DebugVertexData), (void*)offsetof(DebugVertexData, lineWidth)); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + _shader = ShaderUtil::CreateShaderProgram("line.vert", "line.frag"); + + _uniformViewportRes = glGetUniformLocation(_shader, "uViewportRes"); + _uniformModel = glGetUniformLocation(_shader, "model"); + _uniformView = glGetUniformLocation(_shader, "view"); + _uniformProjection = glGetUniformLocation(_shader, "projection"); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + Instance = this; + } + + ~Debug() { + glDeleteVertexArrays(1, &_vba); + glDeleteBuffers(1, &_vbo); + glDeleteProgram(_shader); + } + + void Render(DefaultUniform& uniform) override; + void RenderLines(DefaultUniform& uniform) const; + + static inline Debug* Instance; + + void Line(const glm::vec3& positionFirst, const glm::vec3& positionLast, const Color& color) { + _lines.push_back(DebugLine { positionFirst, positionLast, 1.0f, color }); + } + + void Direction(const glm::vec3& origin, const glm::vec3& direction, const Color& color) { + glm::vec4 capWorldPos = glm::vec4(origin + direction * 1.0f, 1.0f); + + _lines.push_back(DebugLine { origin, capWorldPos, 8.0f, color }); + + glm::vec3 up = glm::vec3(direction[1], direction[2], direction[0]); + + glm::vec3 capOffsets[4] = { + glm::vec3( 1.0f, 1.0f, -1.0f), + glm::vec3(-1.0f, 1.0f, -1.0f), + glm::vec3( 1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, -1.0f, -1.0f) + }; + + glm::mat4 capLocalTf = glm::lookAt(glm::vec3(capWorldPos), origin, up); + + capLocalTf = glm::inverse(capLocalTf); + + float extends = 0.05f; + for(const glm::vec3& offset : capOffsets) { + Line(capWorldPos, capLocalTf * glm::vec4(extends * offset, 1.0f), color); + } + } + + void Point(const glm::vec3& position, const Color& color) { + for(int axis=0;axis<3;axis++) { + glm::vec3 forward = glm::vec3(0.0f); + forward[axis] = 1.0f; + + glm::vec3 lineOrigin = position - forward; + glm::vec3 lineDestinatio = position + forward; + + Line(lineOrigin, lineDestinatio, color); + } + } +}; + diff --git a/RubiksCube/Entity.cpp b/RubiksCube/Entity.cpp new file mode 100644 index 0000000..931d64a --- /dev/null +++ b/RubiksCube/Entity.cpp @@ -0,0 +1,81 @@ +#include "Entity.h" + +void Entity::_UpdateChildTransform() const { + for(auto& child : _children) { + child->_localToWorld = _localToWorld * child->_transform; + child->_worldToLocal = glm::inverse(child->_transform); + + child->_UpdateChildTransform(); + } +} + +Entity::Entity(Entity* parent) : _localToWorld(1.0f), _worldToLocal(1.0f) +{ + _parent = parent; + _transform = glm::mat4(1.0f); + + if (parent == nullptr) + return; + + parent->AddChild(this); + _localToWorld = _parent->_localToWorld; + _worldToLocal = glm::inverse(_localToWorld); +} + +Entity::~Entity() { + _parent->RemoveChild(this); +} + +void Entity::AddChild(Entity* entity) { + _children.insert(entity); +} + +void Entity::RemoveChild(Entity* entity) { + _children.erase(entity); +} + +void Entity::Transform(const glm::mat4& transform) { + glm::mat4 transformLocalBasis = _worldToLocal * transform; + + _transform = transformLocalBasis * _transform; + + if (_parent == nullptr) { + _localToWorld = _transform; + } + else { + _localToWorld = _parent->LocalToWorld() * _transform; + } + + _worldToLocal = glm::inverse(_localToWorld); + _UpdateChildTransform(); +} + +void Entity::TransformLocal(const glm::mat4& transform) +{ + _transform = transform * _transform; + + if (_parent == nullptr) { + _localToWorld = _transform; + } + else { + _localToWorld = _parent->LocalToWorld() * _transform; + } + + _worldToLocal = glm::inverse(_localToWorld); + _UpdateChildTransform(); +} + +void Entity::SetTransform(const glm::mat4& transform) { + _transform = transform; + + if (_parent != nullptr) { + _localToWorld = transform * _parent->_localToWorld; + _worldToLocal = glm::inverse(transform) * _parent->_worldToLocal; + } + else { + _localToWorld = transform; + _worldToLocal = glm::inverse(transform); + } + + _UpdateChildTransform(); +} diff --git a/RubiksCube/Entity.h b/RubiksCube/Entity.h new file mode 100644 index 0000000..52c6d4d --- /dev/null +++ b/RubiksCube/Entity.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +#include "Mesh.h" + +class Entity { +private: + glm::mat4 _transform; + + glm::mat4 _localToWorld; + glm::mat4 _worldToLocal; + + Entity* _parent; + std::unordered_set _children; + + void _UpdateChildTransform() const; + +public: + Entity(Entity* parent); + virtual ~Entity(); + + void AddChild(Entity* entity); + void RemoveChild(Entity* entity); + + Entity* Parent() const { return _parent; } + + void Transform(const glm::mat4& transform); + void TransformLocal(const glm::mat4& transform); + void SetTransform(const glm::mat4& transform); + const glm::mat4& LocalToWorld() const { return _localToWorld; } + const glm::mat4& WorldToLocal() const { return _worldToLocal; } + const glm::mat4& LocalObjectTransform() const { return _transform; } + + virtual void Render(DefaultUniform& uniform) { }; + virtual void Update(double deltaTime) { }; + +}; diff --git a/RubiksCube/InputSystem.cpp b/RubiksCube/InputSystem.cpp index 513c1a0..c898f64 100644 --- a/RubiksCube/InputSystem.cpp +++ b/RubiksCube/InputSystem.cpp @@ -4,9 +4,9 @@ #include -#include +#include -#include +#include void InputSystem::ObserveKey(int key) { _keyCodeDictionaryObserver.emplace(key, KeyObserver(_window, key)); diff --git a/RubiksCube/InputSystem.h b/RubiksCube/InputSystem.h index 6f61370..51b498c 100644 --- a/RubiksCube/InputSystem.h +++ b/RubiksCube/InputSystem.h @@ -2,7 +2,7 @@ #include -#include +#include #include "KeyObserver.h" diff --git a/RubiksCube/Mesh.cpp b/RubiksCube/Mesh.cpp new file mode 100644 index 0000000..49582af --- /dev/null +++ b/RubiksCube/Mesh.cpp @@ -0,0 +1 @@ +#include "Mesh.h" diff --git a/RubiksCube/Mesh.h b/RubiksCube/Mesh.h new file mode 100644 index 0000000..6df299d --- /dev/null +++ b/RubiksCube/Mesh.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include + +#include +#include + +#include "Shader.h" + +class DefaultUniform { +private: + +public: + glm::mat4 model; + glm::mat4 view; + glm::mat4 projection; +}; + +template +class Mesh +{ +private: + TUniform _uniform; + + GLuint _uniformModel; + GLuint _uniformView; + GLuint _uniformProjection; + +protected: + std::vector _vertexData; + + GLuint _vba; + GLuint _vbo; + Shader* shader; + + bool _isVertexDirty; + +public: + + TUniform& Uniform() { return _uniform; } + +public: + Mesh(Shader* shader) : shader(shader), _isVertexDirty(true) { + shader->Initialize(); + + glCreateVertexArrays(1, &_vba); + glCreateBuffers(1, &_vbo); + + _uniformModel = glGetUniformLocation(shader->Reference(), "model"); + _uniformView = glGetUniformLocation(shader->Reference(), "view"); + _uniformProjection = glGetUniformLocation(shader->Reference(), "projection"); + } + + ~Mesh() { + glDeleteVertexArrays(1, &_vba); + glDeleteBuffers(1, &_vbo); + } + + void AddTriangle(const TVertexData& p0, const TVertexData& p1, const TVertexData& p2) { + _vertexData.push_back(p0); + _vertexData.push_back(p1); + _vertexData.push_back(p2); + + _isVertexDirty = true; + } + + void AddQuad(const TVertexData& p0, const TVertexData& p1, const TVertexData& p2, const TVertexData& p3) { + AddTriangle(p0, p1, p2); + AddTriangle(p1, p2, p3); + } + + virtual void Render(const TUniform& uniform) { + shader->Use(); + + glUniformMatrix4fv(_uniformModel, 1, GL_FALSE, glm::value_ptr(uniform.model)); + glUniformMatrix4fv(_uniformView, 1, GL_FALSE, glm::value_ptr(uniform.view)); + glUniformMatrix4fv(_uniformProjection, 1, GL_FALSE, glm::value_ptr(uniform.projection)); + + glBindVertexArray(_vba); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + + if (_isVertexDirty) { + glBufferData(GL_ARRAY_BUFFER, sizeof(TVertexData) * _vertexData.size(), _vertexData.data(), GL_STATIC_DRAW); + + _isVertexDirty = false; + } + + glDrawArrays(GL_TRIANGLES, 0, _vertexData.size()); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + shader->Disable(); + } +}; diff --git a/RubiksCube/MeshData.h b/RubiksCube/MeshData.h deleted file mode 100644 index 6f70f09..0000000 --- a/RubiksCube/MeshData.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/RubiksCube/PartialCube.cpp b/RubiksCube/PartialCube.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/RubiksCube/PartialCube.h b/RubiksCube/PartialCube.h deleted file mode 100644 index e69de29..0000000 diff --git a/RubiksCube/PartitionedCube.cpp b/RubiksCube/PartitionedCube.cpp index 68c8ae4..bf015d6 100644 --- a/RubiksCube/PartitionedCube.cpp +++ b/RubiksCube/PartitionedCube.cpp @@ -1,23 +1,30 @@ #include "PartitionedCube.h" +#include "Debug.h" + #include "Cube.h" -#include -#include +#include +#include -PartitionedCube::PartitionedCube(const glm::vec3& position, Color* sideColors) : _faces { - sideColors[0], - sideColors[1], - sideColors[2], - sideColors[3], - sideColors[4], - sideColors[5], - } +Shader PartitionedCubeMesh::_shader("cube.vert", "cube.frag"); + +Texture PartitionedCubeMesh::_diffuse; +Texture PartitionedCubeMesh::_roughness; +Texture PartitionedCubeMesh::_normal; + +PartitionedCube::PartitionedCube(Cube* parent, const glm::vec3& position, Color* sideColors) : Entity(parent) { - this->_transform = glm::translate(glm::mat4(1.0f), position); + this->TransformLocal(glm::translate(glm::mat4(1.0f), position)); + + this->_transformTem = glm::mat4(1.0f); + + for(int i=0;i<6;i++) { + _faces[i] = sideColors[i]; + } + _animationTime = 0.0f; _animationTimeExtend = 0.0f; - _animationTransformInvoke = _transform; _animationTransform = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); _animationFinished = true; @@ -33,7 +40,7 @@ PartitionedCube::PartitionedCube(const glm::vec3& position, Color* sideColors) : z[axis] = factor; x[(axis+1)%3] = 1.0f; - y[(axis+2)%3] = 1.0f; + y = glm::cross(x, z); glm::vec3 v0 = x * 0.5f + y * 0.5f + 0.5f * z; glm::vec3 v1 = x * -0.5f + y * 0.5f + 0.5f * z; @@ -42,37 +49,109 @@ PartitionedCube::PartitionedCube(const glm::vec3& position, Color* sideColors) : int s = 2 * axis + reverse; // (-x, +x, -y, +y, -z, +z) - _meshData.addFace(v0, v1, v2, v3, sideColors[s]); + glm::vec2 uv0 = glm::vec2(1.0f, 1.0f); + glm::vec2 uv1 = glm::vec2(0.0f, 1.0f); + glm::vec2 uv2 = glm::vec2(1.0f, 0.0f); + glm::vec2 uv3 = glm::vec2(0.0f, 0.0f); + + VertexData d0 = VertexData(v0, z, uv0, sideColors[s]); + VertexData d1 = VertexData(v1, z, uv1, sideColors[s]); + VertexData d2 = VertexData(v2, z, uv2, sideColors[s]); + VertexData d3 = VertexData(v3, z, uv3, sideColors[s]); + + _mesh.AddQuad(d0, d1, d2, d3); factor = -factor; } } + + _mesh.CalculateTangents(); } void PartitionedCube::Update(double deltaTime) { this->_animationTime -= deltaTime; - + if (!_animationFinished && _animationTime <= 0.0f) { - _animationFinished = true; + _animationTime = 0.0f; - _transform = glm::mat4_cast(_animationTransform) * _animationTransformInvoke; + const glm::mat4 finishedAnimationTransform = TransformCustom(); + SetTransform(finishedAnimationTransform); + _animationFinished = true; } } -void PartitionedCube::Transform(glm::mat4 transformation) -{ - this->_transform = transformation * this->_transform; - _animationTransformInvoke = transformation * _animationTransformInvoke; - _animationTransform = glm::quat_cast(transformation * glm::mat4_cast(_animationTransform)); +void PartitionedCube::Render(DefaultUniform& uniform) { + uniform.model = TransformCustom(); + + _mesh.Render(uniform); } -void PartitionedCube::TransformAnimation(const glm::mat4& transform, float duration) { +int toIndex(const glm::ivec3& direction) { + int axis = glm::abs(direction.x * 1 + direction.y * 2 + direction.z * 3) - 1; + int allAxis = direction.x + direction.y + direction.z; + int x = glm::sign(allAxis); + + int reverse = (x + 1) / 2; + + return axis * 2 + reverse; +} + +glm::ivec3 toDirection(int index) { + int axis = index / 2; + int reverse = index % 2; + + glm::ivec3 direction = glm::ivec3(0); + direction[axis] = 1; + + if (reverse == 0) + direction = -direction; + + return direction; +} + +void PartitionedCube::TransformData(const glm::ivec3& axis, int turns) { + Color previousListColors[6]; + for(int i=0;i<6;i++) { + previousListColors[i] = _faces[i]; + } + + glm::mat3 rotations = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f) * turns, glm::vec3(axis)); + + for(int axis=0;axis<3;axis++) { + for(int direction=-1;direction<=1;direction+=2) { + glm::ivec3 position = glm::ivec3(0); + position[axis] = direction; + + glm::vec3 newPosition = rotations * position; + + glm::ivec3 newPositionStable; + newPositionStable.x = std::round(newPosition.x); + newPositionStable.y = std::round(newPosition.y); + newPositionStable.z = std::round(newPosition.z); + + int newIndex = toIndex(newPositionStable); + int index = toIndex(position); + _faces[newIndex] = previousListColors[index]; + } + } +} + +void PartitionedCube::TransformAnimation(const glm::quat& transform, float duration) { if (!_animationFinished) - _transform = glm::mat4_cast(_animationTransform) * _animationTransformInvoke; - - this->_animationTransformInvoke = this->_transform; - this->_animationTransform = glm::quat_cast(transform); + SetTransform(TransformCustom()); + + this->_animationTransform = transform; this->_animationTime = duration; this->_animationTimeExtend = duration; _animationFinished = false; } + +void PartitionedCube::TransformTemp(const glm::mat4& transform) +{ + _transformTem = transform; +} + +void PartitionedCube::UndoTransformTemp() +{ + _transformTem = glm::mat4(1.0f); +} diff --git a/RubiksCube/PartitionedCube.h b/RubiksCube/PartitionedCube.h index 2069dbb..e855da8 100644 --- a/RubiksCube/PartitionedCube.h +++ b/RubiksCube/PartitionedCube.h @@ -4,47 +4,121 @@ #include -#include -#include -#include +#include +#include +#include #include #include +#include "Entity.h" + +#include "Texture.h" +#include "Shader.h" +#include "Mesh.h" + struct VertexData { glm::vec3 position; + glm::vec3 normal; + glm::vec2 uv0; + glm::vec3 tangent; + glm::vec3 bitangent; Color color; - VertexData() : position(glm::vec3(0.0f, 0.0f, 0.0f)), color(Color::WHITE()) { - - } - - VertexData(const glm::vec3& position, Color color) : position(position), color(color) { + VertexData(const glm::vec3& position, const glm::vec3& normal, const glm::vec2 uv0, Color color) : position(position), color(color), normal(normal), uv0(uv0), tangent(0.0f), bitangent(0.0f) { } }; -struct MeshData { - int vertexIndex; - std::vector data; - int triangleIndex; +class PartitionedCubeMesh : public Mesh { +private: + static Shader _shader; + static Texture _diffuse; + static Texture _roughness; + static Texture _normal; - MeshData() { - vertexIndex = 0; - triangleIndex = 0; + GLuint _uniformDiffuse; + GLuint _uniformRoughness; + GLuint _uniformNormal; + +public: + PartitionedCubeMesh() : Mesh(&_shader) { + _shader.Use(); + + glBindVertexArray(_vba); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + + int shd = shader->Reference(); + + _uniformDiffuse = glGetUniformLocation(shd, "diffuse"); + _uniformRoughness = glGetUniformLocation(shd, "roughness"); + _uniformNormal = glGetUniformLocation(shd, "normal"); + + glUniform1i(_uniformDiffuse, 0); + glUniform1i(_uniformRoughness, 1); + glUniform1i(_uniformNormal, 2); + + glVertexAttribPointer(glGetAttribLocation(shd, "position"), 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, position)); + glVertexAttribPointer(glGetAttribLocation(shd, "normal"), 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, normal)); + glVertexAttribPointer(glGetAttribLocation(shd, "uv0"), 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, uv0)); + glVertexAttribPointer(glGetAttribLocation(shd, "tangent"), 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, tangent)); + glVertexAttribPointer(glGetAttribLocation(shd, "bitangent"), 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, bitangent)); + glVertexAttribPointer(glGetAttribLocation(shd, "color"), 4, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, color)); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + _shader.Disable(); } - void addTriangle(glm::vec3 v0, glm::vec3 v1, glm::vec3 v2, Color color) { - data.push_back(VertexData(v0, color)); - data.push_back(VertexData(v1, color)); - data.push_back(VertexData(v2, color)); - vertexIndex += 3; + virtual void Render(const DefaultUniform& uniform) override { + _shader.Use(); - triangleIndex ++; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, _diffuse.Reference()); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _roughness.Reference()); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, _normal.Reference()); + + _shader.Disable(); + + Mesh::Render(uniform); } - void addFace(glm::vec3 v0, glm::vec3 v1, glm::vec3 v2, glm::vec3 v3, Color color) { - addTriangle(v0, v1, v2, color); - addTriangle(v3, v2, v1, color); + static void LoadResources() { + _diffuse = Texture("diffuse.png"); + _roughness = Texture("roughness.png"); + _normal = Texture("normal.png"); + } + + void CalculateTangents() { + for(int i=0;i<_vertexData.size();i += 3) { + VertexData& d0 = _vertexData.at(i + 0); + VertexData& d1 = _vertexData.at(i + 1); + VertexData& d2 = _vertexData.at(i + 2); + + glm::vec3 deltaPos1 = d1.position - d0.position; + glm::vec3 deltaPos2 = d2.position - d0.position; + + glm::vec2 deltaUV1 = d1.uv0 - d0.uv0; + glm::vec2 deltaUV2 = d2.uv0 - d0.uv0; + + float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x); + d0.tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r; + d0.bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r; + d1.tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r; + d1.bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r; + d2.tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r; + d2.bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r; + } } }; @@ -61,40 +135,45 @@ constexpr glm::vec3 SideToDirection[6] = { glm::vec3(0.0f, 0.0f, 1.0f), }; -class PartitionedCube +class Cube; + +class PartitionedCube : public Entity { private: - glm::mat4 _transform; + glm::mat4 _transformTem; - glm::mat4 _animationTransformInvoke; glm::quat _animationTransform; float _animationTime; float _animationTimeExtend; bool _animationFinished; Color _faces[6]; - MeshData _meshData; + PartitionedCubeMesh _mesh; public: - PartitionedCube(const glm::vec3& position, Color sideColors[6]); + PartitionedCube(Cube* parent, const glm::vec3& position, Color* sideColors); - void Update(double deltaTime); + void Update(double deltaTime) override; + void Render(DefaultUniform& uniform) override; - const glm::mat4 Transform() const { + const glm::mat4 TransformCustom() const { // Need to use slerp since rotations are non-linear and would not interpolate this way + // + // However a lerp can be used and then normalized, with not that much of an + // error but being much more performant thus preferred function if (false == _animationFinished) { float lerpFactor = 1.0f - ( _animationTime / _animationTimeExtend); - glm::mat4 animationView = glm::mat4_cast(glm::slerp(glm::quat(1.0f, 0.0f, 0.0f, 0.0f), _animationTransform, lerpFactor)); + glm::mat4 animationView = glm::mat4_cast(glm::normalize(glm::lerp(glm::quat(1.0f, 0.0f, 0.0f, 0.0f), _animationTransform, lerpFactor))); - return animationView * _animationTransformInvoke; + return Parent()->LocalToWorld() * animationView * LocalObjectTransform(); } - return _transform; + return Parent()->LocalToWorld() * _transformTem * LocalObjectTransform(); } - const MeshData& MeshData() const { return _meshData; }; - - void Transform(glm::mat4 transformation); - void TransformAnimation(const glm::mat4& transform, float duration); + void TransformData(const glm::ivec3& axis, int turns); + void TransformAnimation(const glm::quat& transform, float duration); + void TransformTemp(const glm::mat4& transform); + void UndoTransformTemp(); }; diff --git a/RubiksCube/Plane.h b/RubiksCube/Plane.h index 877efcf..cd73d8b 100644 --- a/RubiksCube/Plane.h +++ b/RubiksCube/Plane.h @@ -1,6 +1,6 @@ #pragma once -#include +#include class Plane { diff --git a/RubiksCube/RubiksCube.cpp b/RubiksCube/RubiksCube.cpp index 0b01b4e..0ea8d3f 100644 --- a/RubiksCube/RubiksCube.cpp +++ b/RubiksCube/RubiksCube.cpp @@ -8,6 +8,8 @@ #include "GameInterface.h" #include "SceneInterface.h" +#define STB_IMAGE_IMPLEMENTATION +#include diff --git a/RubiksCube/RubiksCube.vcxproj b/RubiksCube/RubiksCube.vcxproj index 93c882f..43beaec 100644 --- a/RubiksCube/RubiksCube.vcxproj +++ b/RubiksCube/RubiksCube.vcxproj @@ -71,10 +71,10 @@ - $(SolutionDir)/../ExternalResources/glew/include;$(SolutionDir)/../ExternalResources/glfw/include;$(SolutionDir)/../ExternalResources/glm/glm;$(SolutionDir)/../ExternalResources/stb;$(ExternalIncludePath) + $(SolutionDir)/../ExternalResources/glew/include;$(SolutionDir)/../ExternalResources/glfw/include;$(SolutionDir)/../ExternalResources/glm;$(SolutionDir)/../ExternalResources/stb;$(ExternalIncludePath) - $(SolutionDir)/../ExternalResources/glew/include;$(SolutionDir)/../ExternalResources/glfw/include;$(SolutionDir)/../ExternalResources/glm/glm;$(SolutionDir)/../ExternalResources/stb;$(ExternalIncludePath) + $(SolutionDir)/../ExternalResources/glew/include;$(SolutionDir)/../ExternalResources/glfw/include;$(SolutionDir)/../ExternalResources/glm;$(SolutionDir)/../ExternalResources/stb;$(ExternalIncludePath) @@ -110,6 +110,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp17 Console @@ -126,6 +127,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp17 Console @@ -139,30 +141,36 @@ + + + - + + + - + + - + @@ -176,6 +184,40 @@ false + + + false + Document + false + + + false + Document + false + + + + + false + false + + + + + false + false + + + false + false + + + + + false + false + + diff --git a/RubiksCube/RubiksCube.vcxproj.filters b/RubiksCube/RubiksCube.vcxproj.filters index 43eaf9b..b0bb364 100644 --- a/RubiksCube/RubiksCube.vcxproj.filters +++ b/RubiksCube/RubiksCube.vcxproj.filters @@ -36,9 +36,6 @@ Quelldateien - - Quelldateien - Quelldateien @@ -51,6 +48,15 @@ Quelldateien + + Quelldateien + + + Quelldateien + + + Quelldateien + @@ -68,15 +74,6 @@ Headerdateien - - Headerdateien - - - Headerdateien - - - Headerdateien - Headerdateien @@ -92,6 +89,27 @@ Headerdateien + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + @@ -100,5 +118,23 @@ Shaders + + Shaders + + + Shaders + + + Ressourcendateien + + + Ressourcendateien + + + Ressourcendateien + + + Ressourcendateien + \ No newline at end of file diff --git a/RubiksCube/SceneInterface.cpp b/RubiksCube/SceneInterface.cpp index da5886f..13d96af 100644 --- a/RubiksCube/SceneInterface.cpp +++ b/RubiksCube/SceneInterface.cpp @@ -6,9 +6,10 @@ #include "Plane.h" -#include -#include -#include +#include +#include +#include + #include @@ -16,6 +17,20 @@ #include +SceneInterface::SceneInterface() +{ + +} + +SceneInterface::~SceneInterface() +{ + for(Entity* entity : _entities) { + delete entity; + } + + delete currentAction; +} + void SceneInterface::Initialize(GLFWwindow* window) { _inputSystem.SetWindow(window); @@ -39,23 +54,31 @@ void SceneInterface::Initialize(GLFWwindow* window) _inputSystem.ObserveKey(GLFW_KEY_KP_9); _wasMouseDown = false; - - this->_shaderProgram = ShaderUtil::CreateShaderProgram("cube.vert", "cube.frag"); - this->_transformLocation = glGetUniformLocation(this->_shaderProgram, "transformation"); - - glGenVertexArrays(1, &this->_arrayBufferObject); - glGenBuffers(1, &this->_vertexBufferObject); - - glBindVertexArray(_arrayBufferObject); - glBindBuffer(GL_ARRAY_BUFFER, this->_vertexBufferObject); - - glVertexAttribPointer(glGetAttribLocation(_shaderProgram, "position"), 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, position)); - glEnableVertexAttribArray(0); - glVertexAttribPointer(glGetAttribLocation(_shaderProgram, "color"), 4, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, color)); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); + _debug = new Debug(window); + + PartitionedCubeMesh::LoadResources(); + + _camera = new Camera(window, &_inputSystem); + Cube* cube = new Cube(); + _cube = cube; + + _entities.push_back(_debug); + _entities.push_back(_camera); + _entities.push_back(_cube); + + for(PartitionedCube** cube = _cube->Children();cube != _cube->Children() + _cube->ChildPartitionsCount;cube++) { + _entities.push_back(*cube); + } + + std::cout << "Controls: " << std::endl; + std::cout << "Numpad 1, 2, 3, 7, 8, 9 + Shift Rotate X Axis Down Up View" << std::endl; + std::cout << "Numpad 1, 4, 7, 3, 6, 9 Rotate Y Axis Left Right View" << std::endl; + std::cout << "Arrows Up, Down, Right, Left Rotate Camera relative lookat" << std::endl; + std::cout << "Space Reset Look Changes" << std::endl; + std::cout << "Mouse Scroll Vertically Zoom" << std::endl; + std::cout << "Left Mouse Rotate Cube Planes" << std::endl; + std::cout << "Right Mouse Rotate Camera" << std::endl; } glm::vec3 basisCoordinateVectors[] = { @@ -113,33 +136,19 @@ void IntersectLinePlane(const glm::vec3& lineStart, const glm::vec3& lineDirecti } } -void SceneInterface::EnqueueAction(const Action& action) { +void SceneInterface::EnqueueAction(Action* action) { _actions.push(action); } void SceneInterface::Update(float deltaTime) { - _cube.Update(deltaTime); _inputSystem.Update(); - if (_inputSystem.WasKeyPressed(GLFW_KEY_SPACE)) - _orientation = glm::quat(1.0f, glm::vec3(0.0f, 0.0f, 0.0f)); + for(Entity* entity : _entities) { + entity->Update(deltaTime); + } - glm::fvec2 velocity(0.0f, 0.0f); - if (_inputSystem.IsKeyPressed(GLFW_KEY_UP)) - velocity.x = glm::radians(90.0f); - if (_inputSystem.IsKeyPressed(GLFW_KEY_DOWN)) - velocity.x = glm::radians(-90.0f); - - if (_inputSystem.IsKeyPressed(GLFW_KEY_RIGHT)) - velocity.y = glm::radians(90.0f); - if (_inputSystem.IsKeyPressed(GLFW_KEY_LEFT)) - velocity.y = glm::radians(-90.0f); - - glm::quat velocityQuaternion = glm::quat(0.0f, glm::vec3(velocity.x, velocity.y, 0.0f)); - - _orientation += 0.5f * (float)deltaTime * velocityQuaternion * _orientation; - _orientation = glm::normalize(_orientation); + // int spinIndex = 0; @@ -170,49 +179,98 @@ void SceneInterface::Update(float deltaTime) int axisIndex = spinAxis.x == 0; // x => 0, y => 1 int spinIndex = keyToIndex[axisIndex][rotationKey - 1]; - if (reverse) - spinAxis *= -1; + int reverseSign = reverse ? -1 : 1; + + spinAxis *= reverseSign; + spinIndex *= reverseSign; + + glm::mat3 cubeMat = glm::mat3(_cube->LocalToWorld()) * glm::mat3(_camera->View()); + + glm::mat3 orthogonalized = cubeMat; + for(int column=0;column<3;column++) { + int nearestCardinalisedAxis = 0; + for(int i=0;i<3;i++) { + if (glm::abs(orthogonalized[column][i]) > glm::abs(orthogonalized[column][nearestCardinalisedAxis])) { + nearestCardinalisedAxis = i; + } + } + + for(int columnToPlace=0;columnToPlace<3;columnToPlace++) { + for(int i=0;i<3;i++) { + if (columnToPlace == column) { + if (nearestCardinalisedAxis == i) { + orthogonalized[columnToPlace][i] = glm::sign(orthogonalized[columnToPlace][i]); + } + else { + orthogonalized[columnToPlace][i] = 0.0f; + } + } + else { + if (nearestCardinalisedAxis == i) { + orthogonalized[columnToPlace][i] = 0.0f; + } + } + } + } + } + + glm::mat3 screenToCube = glm::inverse(orthogonalized); + + spinAxis = screenToCube * spinAxis; + int direction = spinAxis.x + spinAxis.y + spinAxis.z; + + EnqueueAction(new ActionSpinDefault(_cube, spinAxis, spinIndex, 1.0f)); // _cube.TransformAnimation(cubeSpinAxis, spinIndex, rotation, 1.0f); - EnqueueAction(Action(spinAxis, spinIndex, 1.0f)); - std::cout << glm::to_string(spinAxis) << " " << spinIndex << std::endl; } - if (!_wasMouseDown && _inputSystem.IsLeftMouseDown()) { - OnDragStart(); + if (currentAction == nullptr) { + if (!_wasMouseDown && _inputSystem.IsLeftMouseDown()) { + OnDragStart(); + } + else if (_wasMouseDown && !_inputSystem.IsLeftMouseDown()) { + OnDragStop(); + } + else if (_wasMouseDown && _inputSystem.IsLeftMouseDown()) { + OnDrag(deltaTime); + } + + _wasMouseDown = _inputSystem.IsLeftMouseDown(); } - else if (_wasMouseDown && !_inputSystem.IsLeftMouseDown()) { - OnDragStop(); - } - else if (_wasMouseDown && _inputSystem.IsLeftMouseDown()) { - OnDrag(deltaTime); + + if (currentAction != nullptr) { + currentAction->duration -= deltaTime; } - _wasMouseDown = _inputSystem.IsLeftMouseDown(); + if (currentAction != nullptr && currentAction->duration > 0.0f) { + return; + } - currentAction.duration -= deltaTime; - if (currentAction.duration <= 0.0f && _actions.size() > 0) { - Action& action = _actions.front(); + if (_actions.size() > 0) { + delete currentAction; + + Action* action = _actions.front(); _actions.pop(); ApplyAction(action); } + else { + currentAction = nullptr; + } } void SceneInterface::OnDragStart() { _inputSystem.GetMousePos(_initialMouseLocation); - Plane planes[6]; - glm::vec3 lineStart; glm::vec3 lineDirection; - glm::mat4 viewPerspective = _perspective * _view; + glm::mat4 viewPerspective = _camera->Projection() * _camera->View(); _inputSystem.GetPickingRay(viewPerspective, lineStart, lineDirection); - glm::mat4 transform = glm::mat4(1.0f); + glm::mat4 transform = _cube->LocalToWorld(); for(int axis=0;axis<3;axis++) { for(int direction=-1;direction<=1;direction+=2) { @@ -252,7 +310,7 @@ void SceneInterface::OnDrag(double deltaTime) { glm::vec3 lineStart; glm::vec3 lineDirection; - glm::mat4 transform = _perspective * _view; + glm::mat4 transform = _camera->Projection() * _camera->View(); _inputSystem.GetPickingRay(transform, lineStart, lineDirection); float intersectionDistance = -1.0f; @@ -269,7 +327,7 @@ void SceneInterface::OnDrag(double deltaTime) { float dragAbsDistance = abs(glm::distance(glm::vec2(0.0f), directionPlane)); if (dragAbsDistance >= MIN_DRAG_MAGNITUDE) { - glm::vec3 mousePosition = glm::inverse(planeTransform) * glm::vec4(_mousePositionPlane, 0.0f, 1.0f); + glm::vec3 mousePosition = planeTransform * glm::vec4(_mousePositionPlane, 0.0f, 1.0f); glm::vec3 direction = glm::inverse(planeTransform) * glm::vec4(directionPlane, 0.0f, 0.0f); @@ -279,113 +337,76 @@ void SceneInterface::OnDrag(double deltaTime) { axisSingle = orthogonalise(axisSingle); float projection = glm::dot(mousePosition, axisSingle); - + int index = floor((projection + 0.5f)); glm::ivec3 axis = orthogonalise(axisSingle); - std::cout << glm::to_string(axisSingle) << std::endl; - std::cout << glm::to_string(direction) << std::endl; - std::cout << glm::to_string(axis) << std::endl; + _cube->UndoTransformTemp(); - _cube.TransformTemp(axis, index, glm::rotate(glm::mat4(1.0f), glm::radians(abs(glm::distance(glm::vec2(0.0f), directionPlane))), axisSingle)); + float angle = glm::radians(90.0f * ( 1.0f / 3.0f ) * abs(glm::distance(glm::vec2(0.0f), directionPlane))); + + _cube->TransformTemp(axis, index, glm::rotate(glm::mat4(1.0f), angle, axisSingle)); + _spinIndex = index; + _spinAxis = axis; + _spinDelta = angle; } } +const float rotations = 2 * glm::pi(); + +const float quarterRotation = rotations * 0.25f; + void SceneInterface::OnDragStop() { + // Undo any temporarilies + _cube->UndoTransformTemp(); + + // Transform Instantly to current angle + _cube->Transform(_spinAxis, _spinIndex, glm::rotate(glm::mat4(1.0f), _spinDelta, glm::vec3(_spinAxis))); + + float angleNormalized = std::fmodf(_spinDelta, quarterRotation); + + float remainingAngle = 0.5f - glm::abs(angleNormalized - quarterRotation * 0.5f); + float remainingFactor = remainingAngle / quarterRotation; + + int rotations = round(_spinDelta / quarterRotation); + + // remaining rotation animating + if (_spinDelta > 0.0f && rotations > 0) { + EnqueueAction(new ActionSpinAfterDragging(_cube, _spinAxis, _spinIndex, ( quarterRotation * rotations ) - _spinDelta, rotations, remainingFactor * 1.0f)); + } + else { + EnqueueAction(new ActionSpinAfterDragging(_cube, _spinAxis, _spinIndex, -( _spinDelta ), rotations, remainingFactor * 1.0f)); + } } -void SceneInterface::ApplyAction(const Action& action) { +void SceneInterface::ApplyAction(Action* action) { currentAction = action; - glm::mat3 cubeMat = _cube.Transform() * glm::mat4_cast(_orientation); - - glm::mat3 orthogonalized = cubeMat; - for(int column=0;column<3;column++) { - int nearestCardinalisedAxis = 0; - for(int i=0;i<3;i++) { - if (glm::abs(orthogonalized[column][i]) > glm::abs(orthogonalized[column][nearestCardinalisedAxis])) { - nearestCardinalisedAxis = i; - } - } - - for(int columnToPlace=0;columnToPlace<3;columnToPlace++) { - for(int i=0;i<3;i++) { - if (columnToPlace == column) { - if (nearestCardinalisedAxis == i) { - orthogonalized[columnToPlace][i] = glm::sign(orthogonalized[columnToPlace][i]); - } - else { - orthogonalized[columnToPlace][i] = 0.0f; - } - } - else { - if (nearestCardinalisedAxis == i) { - orthogonalized[columnToPlace][i] = 0.0f; - } - } - } - } - } - - glm::vec3 axis = action.axis; - - int direction = axis.x + axis.y + axis.z; - - axis = glm::inverse(orthogonalized) * axis; - - int index = action.index; - index *= direction; - - glm::mat4 rotationMat = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), axis); - for(int i=0;i<3;i++) { - for(int g=0;g<3;g++) { - rotationMat[i][g] = round(rotationMat[i][g]); - } - } - glm::imat3x3 rotation = glm::imat3x3(rotationMat); - - _cube.TransformAnimation(axis, index, rotation, action.duration); + action->Invoke(); } void SceneInterface::Render(float aspectRatio) { - glUseProgram(_shaderProgram); - glBindVertexArray(this->_arrayBufferObject); + if (_camera == nullptr) + return; - glBindBuffer(GL_ARRAY_BUFFER, this->_vertexBufferObject); + DefaultUniform uniform; + uniform.view = _camera->View(); + uniform.projection = _camera->Projection(); - _view = glm::lookAt(glm::vec3(0.0f, 0.0f, 4.75f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)) * glm::mat4_cast(_orientation); - _perspective = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f); + for(Entity* entity : _entities) { + uniform.model = entity->LocalToWorld(); - auto transformation = _perspective * _view; + entity->Render(uniform); + } // std::cout << "perspective: " << glm::to_string(glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f)) << std::endl; // std::cout << "view : " << glm::to_string(glm::lookAt(glm::vec3(0.0f, 0.0f, -3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f))) << std::endl; // std::cout << "model : " << glm::to_string(_cube.Transform()) << std::endl; - - for(int i=0;i<_cube.ChildPartitionsCount;i++) { - const PartitionedCube* cube = _cube.Children()[i]; - - const MeshData& meshData = cube->MeshData(); - - const glm::vec3& position = meshData.data[0].position; - - auto localTransform = transformation * cube->Transform(); - - glUniformMatrix4fv(_transformLocation, 1, GL_FALSE, glm::value_ptr(localTransform)); - glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData) * meshData.vertexIndex, meshData.data.data(), GL_STATIC_DRAW); - glDrawArrays(GL_TRIANGLES, 0, meshData.vertexIndex); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - glUseProgram(0); } void SceneInterface::ClearResources() { - glDeleteBuffers(1, &_vertexBufferObject); - glDeleteVertexArrays(1, &_arrayBufferObject); - glDeleteProgram(_shaderProgram); + } diff --git a/RubiksCube/SceneInterface.h b/RubiksCube/SceneInterface.h index 193de17..f0c71e8 100644 --- a/RubiksCube/SceneInterface.h +++ b/RubiksCube/SceneInterface.h @@ -2,69 +2,109 @@ #include +#include "Debug.h" #include "Plane.h" #include "InputSystem.h" #include "GameInterface.h" +#include "Camera.h" #include "Shader.h" #include "Cube.h" -#include +#include #include struct Action { - glm::ivec3 axis; - int index; +protected: + Cube* _cube; + +public: double duration; - Action() : axis(glm::ivec3(0)), index(0), duration(0.0f) { } + Action(Cube* cube, double duration) : _cube(cube), duration(duration) { } + virtual void Invoke() = 0; +}; - Action(const glm::ivec3& axis, int index, double duration) - : axis(axis), index(index), duration(duration) { +struct ActionSpinDefault : public Action { + glm::ivec3 axis; + int index; + + ActionSpinDefault() : Action(nullptr, 0.0f), axis(glm::ivec3(0)), index(0) { } + + ActionSpinDefault(Cube* cube, const glm::ivec3& axis, int index, double duration) + : Action(cube, duration), axis(axis), index(index) { } + + void Invoke() override { + _cube->TransformAnimation(axis, index, glm::radians(90.0f), duration); + + _cube->TransformData(axis, index, 1); + } +}; + +struct ActionSpinAfterDragging : public Action { + glm::ivec3 axis; + int index; + float angle; + int turns; + + ActionSpinAfterDragging() : Action(nullptr, 0.0f), axis(glm::ivec3(0)), index(0), angle(0.0f), turns(0) { } + + ActionSpinAfterDragging(Cube* cube, const glm::ivec3& axis, int index, float angle, int turns, double duration) + : Action(cube, duration), axis(axis), index(index), angle(angle), turns(turns) { + + } + + void Invoke() override { + _cube->TransformAnimation(axis, index, angle, duration); + + _cube->TransformData(axis, index, turns); + } }; class SceneInterface : public GameInterface { private: - Cube _cube; - std::queue _actions; - Action currentAction; + Cube* _cube; + Camera* _camera; + + std::vector _entities; + + std::queue _actions; + Action* currentAction; + + Debug* _debug; InputSystem _inputSystem; - glm::quat _orientation; - bool _wasMouseDown; glm::vec2 _initialMouseLocation; Plane _plane; glm::vec2 _mousePositionPlane; - glm::mat4 _view; - glm::mat4 _perspective; - - GLuint _shaderProgram; - GLuint _transformLocation; - GLuint _arrayBufferObject; - GLuint _vertexBufferObject; + int _spinIndex; + glm::ivec3 _spinAxis; + float _spinDelta; const float MIN_DRAG_MAGNITUDE = 0.16f; - void ApplyAction(const Action& action); + void ApplyAction(Action* action); void OnDragStart(); void OnDrag(double deltaTime); void OnDragStop(); public: + SceneInterface(); + ~SceneInterface(); + void Initialize(GLFWwindow* window) override; void Update(float deltaTime) override; void Render(float aspectRatio) override; - void EnqueueAction(const Action& action); + void EnqueueAction(Action* action); void ClearResources() override; }; - diff --git a/RubiksCube/Settings.h b/RubiksCube/Settings.h new file mode 100644 index 0000000..a708389 --- /dev/null +++ b/RubiksCube/Settings.h @@ -0,0 +1,12 @@ +#pragma once + +const bool ENABLE_DEBUG = true; + +namespace Settings { + const float TURN_ANIMATION_DURATION = 1.25f; + const float MIN_TURN_DISTANCE = 3.0f; + + const float MIN_CAMERA_DISTANCE = 4.2f; + const float MAX_CAMERA_DISTANCE = 10.0f; + const float CAMERA_DISTANCE_START = 8.0f; +} diff --git a/RubiksCube/Shader.cpp b/RubiksCube/Shader.cpp deleted file mode 100644 index 43a1b8e..0000000 --- a/RubiksCube/Shader.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Shader.h" diff --git a/RubiksCube/Shader.h b/RubiksCube/Shader.h index 9eac2f8..0639e09 100644 --- a/RubiksCube/Shader.h +++ b/RubiksCube/Shader.h @@ -1,5 +1,45 @@ #pragma once -class Shader -{ -}; +#include + +#include "ShaderUtil.h" + +class Shader { +private: + const char* _vert; + const char* _frag; + + GLuint _id; + +public: + Shader(const char* vert, const char* frag) : _vert(vert), _frag(frag), _id(0) { + + } + + ~Shader() { + if (_id != 0) { + glDeleteProgram(_id); + } + } + + GLuint Reference() const { return _id; } + + void Initialize() { + if (_id != 0) + return; + + _id = ShaderUtil::CreateShaderProgram(_vert, _frag); + } + + void Use() { + Initialize(); + + glUseProgram(_id); + } + + void Disable() { + Initialize(); + + glUseProgram(0); + } +}; diff --git a/RubiksCube/Texture.h b/RubiksCube/Texture.h new file mode 100644 index 0000000..2cd8c6c --- /dev/null +++ b/RubiksCube/Texture.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include + +#include + +class Texture { +private: + GLuint _id; + +protected: + const char* _texture; + +public: + GLuint Reference() const { + return _id; + } + +public: + Texture() : _id(0) { } + + Texture(const char* file) { + int width, height, len; + + stbi_set_flip_vertically_on_load(1); + unsigned char* data = stbi_load(file, &width, &height, &len, STBI_rgb_alpha); + + glGenTextures(1, &_id); + glBindTexture(GL_TEXTURE_2D, _id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + stbi_image_free(data); + } + + Texture(const Texture& other) : Texture(other._texture) { + + } + + Texture& operator=(const Texture& other) { + if (this == &other) + return *this; + + Texture copy(other._texture); + this->_id = other._id; + this->_texture = other._texture; + + copy._id = 0; + copy._texture = nullptr; + + return *this; + } + + Texture(Texture&& other) noexcept { + _id = other._id; + _texture = other._texture; + + other._id = 0; + other._texture = nullptr; + } + + Texture& operator= (Texture&& other) noexcept { + if (this == &other) + return *this; + + this->_id = other._id; + this->_texture = other._texture; + + other._id = 0; + other._texture = nullptr; + } + + ~Texture() { + if (_id == 0) + return; + + glDeleteTextures(1, &_id); + } + + void Use() const { + glBindTexture(GL_TEXTURE_2D, _id); + } + + void Disable() const { + glBindTexture(GL_TEXTURE_2D, 0); + } +}; diff --git a/RubiksCube/VertexData.h b/RubiksCube/VertexData.h deleted file mode 100644 index 6f70f09..0000000 --- a/RubiksCube/VertexData.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/RubiksCube/cube.frag b/RubiksCube/cube.frag index 94d6252..0a77914 100644 --- a/RubiksCube/cube.frag +++ b/RubiksCube/cube.frag @@ -1,9 +1,45 @@ #version 330 +uniform mat4 view; + out vec4 color; in vec4 vertexColor; +in mat3 TBN; + +in vec3 fPos; +in vec2 TCordinate; + +uniform sampler2D diffuse; +uniform sampler2D roughness; +uniform sampler2D normal; + +const vec3 lightColor = vec3(1.0f, 1.0f, 1.0f); +const vec3 lightDir = vec3(-1.0f, -1.0f, 1.0f); +const float ambient = 0.24f; +const float specularValue = 1.0f; + void main() { - color = vertexColor; + vec4 diffuseBase = texture(diffuse, TCordinate); + + vec3 normal = texture(normal, TCordinate).rgb; + normal = normal * 2.0f - 1.0f; + normal = normalize(mat3(view) * TBN * normal); + + vec3 lightNormal = normalize(vec3(view * vec4(lightDir, 0.0f))); + + float diffValue = max(dot(normal, lightNormal), 0.0f); + float diffuse = diffValue; + + vec3 viewDir = normalize(-fPos); + + vec3 reflectDir = reflect(-lightNormal, normal); + + float spec = pow(max(dot(viewDir, reflectDir), 0.0f), 16); + float specular = specularValue * spec; + + float roughness = texture(roughness, TCordinate).g; + + color = vertexColor * diffuseBase * vec4((ambient + diffuse) * lightColor, 1.0f) + vec4(roughness * specular * lightColor, 1.0f); } diff --git a/RubiksCube/cube.vert b/RubiksCube/cube.vert index 2c8acd7..d60562b 100644 --- a/RubiksCube/cube.vert +++ b/RubiksCube/cube.vert @@ -1,13 +1,34 @@ #version 330 -uniform mat4 transformation; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; layout (location=0) in vec3 position; -layout (location=1) in vec4 color; +layout (location=1) in vec3 normal; +layout (location=2) in vec2 uv0; +layout (location=3) in vec3 tangent; +layout (location=4) in vec3 bitangent; +layout (location=5) in vec4 color; out vec4 vertexColor; +out mat3 TBN; + +out vec3 fPos; +out vec2 TCordinate; + void main() { - gl_Position = transformation * vec4(position, 1.0f); + gl_Position = projection * view * model * vec4(position, 1.0f); + + fPos = vec3(view * model * vec4(position, 1.0f)); + vertexColor = color; + + vec3 T = normalize(vec3(model * vec4(tangent, 0.0f))); + vec3 B = normalize(vec3(model * vec4(bitangent, 0.0f))); + vec3 N = normalize(vec3(model * vec4(normal, 0.0f))); + TBN = mat3(T, B, N); + + TCordinate = uv0; } diff --git a/RubiksCube/diffuse.png b/RubiksCube/diffuse.png new file mode 100644 index 0000000..d5169fd Binary files /dev/null and b/RubiksCube/diffuse.png differ diff --git a/RubiksCube/diffuse_test.png b/RubiksCube/diffuse_test.png new file mode 100644 index 0000000..bd2ea6b Binary files /dev/null and b/RubiksCube/diffuse_test.png differ diff --git a/RubiksCube/line.frag b/RubiksCube/line.frag new file mode 100644 index 0000000..e072dd6 --- /dev/null +++ b/RubiksCube/line.frag @@ -0,0 +1,19 @@ +#version 330 + +const float blend = 1.5f; + +in float lineStrength; + +in vec4 color; +in float lineWidth; + +out vec4 fragColor; + +void main(void) +{ + vec4 col = color; + float d = length(vec2(lineStrength) - gl_FragCoord.xy); + float w = lineWidth; + + fragColor = col; +} diff --git a/RubiksCube/line.vert b/RubiksCube/line.vert new file mode 100644 index 0000000..45aa850 --- /dev/null +++ b/RubiksCube/line.vert @@ -0,0 +1,69 @@ +#version 330 + +uniform vec2 uViewportRes; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 tangent; +layout (location = 2) in vec4 iColor; +layout (location = 3) in float iLineWidth; + +out vec4 color; +out float lineWidth; +out float lineStrength; + +void main(void) { + int index = gl_VertexID % 6; + float orientation = 1.0f + ( (index % 2) * -2 ); + + vec4 positionScreen = projection * view * model * vec4(position, 1.0f); + vec4 directionScreen = projection * view * model * vec4(position + tangent, 0.0f); + + vec4 normal = positionScreen - directionScreen; + + positionScreen.xyz /= positionScreen.w; + positionScreen.xy *= (uViewportRes * 0.5f); + + float aspectRatio = projection[1][1] / projection[0][0]; + + normal.xyz /= normal.w; + positionScreen += vec4(normalize(normal.yx * vec2(-1.0f, 1.0f) * orientation), 0.0f, 0.0f) * 0.5f * iLineWidth; + + positionScreen.xy /= (uViewportRes * 0.5f); + positionScreen.xyz *= positionScreen.w; + + + gl_Position = positionScreen; + + lineWidth = iLineWidth; + lineStrength = orientation; + color = iColor; +} + +void mainBackup(void) +{ + float aspectRatio = 0.0f; + + int index = gl_VertexID % 6; + float orientation = 1 + ( (index % 2) * -2 ); + + vec4 positionCamera = view * model * vec4(position, 1.0f); + + float distance = -position.z; + + vec4 tv = projection * view * model * vec4(position + tangent, 0.0f); + + vec4 v = vec4(normalize(tv.yx) * orientation * vec2(-1.0f, 1.0f), 0.0f, 0.0f) * distance; + + vec4 direction = positionCamera - tv; + + direction.xy = direction.yx * vec2(-1.0f, 1.0f) * orientation; + + gl_Position = (projection * positionCamera) + vec4(direction.xy, 0.0f, 0.0f); + + lineWidth = iLineWidth; + lineStrength = orientation; + color = vec4(iColor.xyz, 1.0f); +} diff --git a/RubiksCube/normal.png b/RubiksCube/normal.png new file mode 100644 index 0000000..c791942 Binary files /dev/null and b/RubiksCube/normal.png differ diff --git a/RubiksCube/roughness.png b/RubiksCube/roughness.png new file mode 100644 index 0000000..dbf5af4 Binary files /dev/null and b/RubiksCube/roughness.png differ diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..e6993ac --- /dev/null +++ b/readme.txt @@ -0,0 +1,39 @@ +Rubiks Cube Projekt + + + +Steuerung: + +Linke Maustaste Halten und Ziehen Cube angeklickte Scheibe drehen +Rechte Maustaste Halten und Ziehen Kamera drehen +Scrollrad Zoom + +Shift + Numpad 1, 2, 3 ( Numlock ausschalten! ) Cube Horizontal nach unten +Shift + Numpad 7, 8, 9 ( Numlock ausschalten! ) Cube Horizontal nach oben +Numpad 1, 4, 7 Cube vertikal links drehen +Numpad 9, 6, 3 Cube vertikal rechts drehen + +Space Ansicht ( Drehung ) zurücksetzen + +Kurze Übersicht der Welt: + Die Anwendung wird gestartet, mit der Kamera von vorne auf den Cube gerichtet. + Der Cube bleibt im World-Space ohne Veränderungen. + Die Kamera dreht sich um den Cube herum. + Die Lichtquelle leuchtet von unten links und von vorne. + +Features: + - Logische Repräsentation + - Tastatursteuerung + - + Animations + - Kamerasteuerung + - Maussteuerung Kamerarelativ + - + Animations + + + +Rendering: + - Directional Lighting + - Blinn-Shading + - Diffuse Texturierung + - Normal Map mit Textur als Normal + - Specular Map