Spieleprogrammierung/RubiksCube/SceneInterface.cpp
2025-01-19 16:03:36 +01:00

411 lines
11 KiB
C++

#include "ShaderUtil.h"
#include "SceneInterface.h"
#include "PartitionedCube.h"
#include "Plane.h"
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/gtx/string_cast.hpp>
#include <GLFW/glfw3.h>
#include <iostream>
SceneInterface::SceneInterface() {
}
SceneInterface::~SceneInterface() {
std::reverse(_entities.begin(), _entities.end());
for(Entity* entity : _entities) {
delete entity;
}
delete currentAction;
}
void SceneInterface::Initialize(GLFWwindow* window) {
_inputSystem.SetWindow(window);
_inputSystem.ObserveKey(GLFW_KEY_SPACE);
_inputSystem.ObserveKey(GLFW_KEY_UP);
_inputSystem.ObserveKey(GLFW_KEY_DOWN);
_inputSystem.ObserveKey(GLFW_KEY_LEFT);
_inputSystem.ObserveKey(GLFW_KEY_RIGHT);
_inputSystem.ObserveKey(GLFW_KEY_LEFT_SHIFT);
_inputSystem.ObserveKey(GLFW_KEY_KP_1);
_inputSystem.ObserveKey(GLFW_KEY_KP_2);
_inputSystem.ObserveKey(GLFW_KEY_KP_3);
_inputSystem.ObserveKey(GLFW_KEY_KP_4);
_inputSystem.ObserveKey(GLFW_KEY_KP_5);
_inputSystem.ObserveKey(GLFW_KEY_KP_6);
_inputSystem.ObserveKey(GLFW_KEY_KP_7);
_inputSystem.ObserveKey(GLFW_KEY_KP_8);
_inputSystem.ObserveKey(GLFW_KEY_KP_9);
_wasMouseDown = false;
_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[] = {
glm::vec3(1.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f),
glm::vec3(0.0f, 0.0f, 1.0f)
};
glm::ivec3 orthogonalise(const glm::vec3& value) {
int minimum = 0;
for(int i=0;i<3;i++) {
if (abs(value[i])>abs(value[minimum]))
minimum = i;
}
glm::ivec3 orthogon = glm::vec3(0.0f);
orthogon[minimum] = glm::sign(value[minimum]);
return orthogon;
}
void roundAllScalars(glm::mat3& transforms) {
for(int x=0;x<3;x++) {
for(int y=0;y<3;y++) {
transforms[x][y] = round(transforms[x][y]);
}
}
}
glm::imat3x3 orthogonaliseMatrix(const glm::mat3& value) {
glm::mat3 temp = value;
roundAllScalars(temp);
glm::imat3x3 result;
for(int i=0;i<3;i++) {
result[i] = orthogonalise(value[i]);
for(int adjacent=i+1;adjacent<3;adjacent++) {
result[adjacent] = result[adjacent] * ( glm::abs(result[i]) * - 1 ) + 2;
}
}
return result;
}
void IntersectLinePlane(const glm::vec3& lineStart, const glm::vec3& lineDirection, const glm::vec3& planeOrigin, const glm::vec3& planeNormal, float& distance) {
glm::vec3 w = lineStart - planeOrigin;
float intersectionDistance = glm::dot(planeOrigin - lineStart, planeNormal) / glm::dot(lineDirection, planeNormal);
if (intersectionDistance > 0) {
distance = intersectionDistance;
}
}
void SceneInterface::EnqueueAction(Action* action) {
_actions.push(action);
}
void SceneInterface::Update(float deltaTime) {
_inputSystem.Update();
for(Entity* entity : _entities) {
entity->Update(deltaTime);
}
// int spinIndex = 0;
// Gather Axis (+x, +y)
glm::ivec3 spinAxis;
if (_inputSystem.IsKeyPressed(GLFW_KEY_LEFT_SHIFT))
spinAxis = glm::ivec3(1, 0, 0);
else
spinAxis = glm::ivec3(0, 1, 0);
// Numpad
int rotationKey = -1;
for(int i=1;i<10;i++) {
if (_inputSystem.WasKeyPressed(GLFW_KEY_KP_0 + i)) {
rotationKey = i;
}
}
if (rotationKey != -1) {
bool reverseVertical = spinAxis.x == 1 && (rotationKey == 7 || rotationKey == 8 || rotationKey == 9);
bool reverseHorizontal = spinAxis.y == 1 && (rotationKey == 1 || rotationKey == 4 || rotationKey == 7);
static const int keyToIndex[2][9] = { { -1, 0, 1, 0, 0, 0, -1, 0, 1 }, { -1, 0, -1, 0, 0, 0, 1, 0, 1 } };
bool reverse = reverseVertical || reverseHorizontal;
// spin which lookup
int axisIndex = spinAxis.x == 0; // x => 0, y => 1
int spinIndex = keyToIndex[axisIndex][rotationKey - 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);
std::cout << glm::to_string(spinAxis) << " " << spinIndex << std::endl;
}
if (currentAction == nullptr) {
if (!_wasMouseDown && _inputSystem.IsLeftMouseDown()) {
OnDragStart();
}
else if (_wasMouseDown && !_inputSystem.IsLeftMouseDown()) {
OnDragStop();
}
else if (_wasMouseDown && _inputSystem.IsLeftMouseDown()) {
OnDrag(deltaTime);
}
_wasMouseDown = _inputSystem.IsLeftMouseDown();
}
if (currentAction != nullptr) {
currentAction->duration -= deltaTime;
}
if (currentAction != nullptr && currentAction->duration > 0.0f) {
return;
}
if (_actions.size() > 0) {
delete currentAction;
Action* action = _actions.front();
_actions.pop();
ApplyAction(action);
}
else {
currentAction = nullptr;
}
}
void SceneInterface::OnDragStart() {
_inputSystem.GetMousePos(_initialMouseLocation);
glm::vec3 lineStart;
glm::vec3 lineDirection;
glm::mat4 viewPerspective = _camera->Projection() * _camera->View();
_inputSystem.GetPickingRay(viewPerspective, lineStart, lineDirection);
glm::mat4 transform = _cube->LocalToWorld();
for(int axis=0;axis<3;axis++) {
for(int direction=-1;direction<=1;direction+=2) {
Plane plane(transform, axis, direction);
if (glm::dot(plane.Normal(), lineDirection) > 0)
continue;
float intersectionDistance = -1.0f;
IntersectLinePlane(lineStart, lineDirection, plane.Origin(), plane.Normal(), intersectionDistance);
if (intersectionDistance == -1.0f) {
continue;
}
const glm::mat4& planeTransform = plane.Transform();
glm::vec4 intersectionPoint = glm::vec4(lineStart + lineDirection * intersectionDistance, 1.0f);
glm::vec4 intersectionPlane = glm::inverse(planeTransform) * intersectionPoint;
bool onPlane = abs(intersectionPlane.x) < 1.501f && abs(intersectionPlane.y) < 1.501f;
if (!onPlane) {
continue;
}
_spinDelta = 0.0f;
_spinIndex = -1;
_plane = plane;
_mousePositionPlane = intersectionPlane;
}
}
}
void SceneInterface::OnDrag(double deltaTime) {
glm::vec2 currentMousePosition;
_inputSystem.GetMousePos(currentMousePosition);
glm::vec3 lineStart;
glm::vec3 lineDirection;
glm::mat4 transform = _camera->Projection() * _camera->View();
_inputSystem.GetPickingRay(transform, lineStart, lineDirection);
float intersectionDistance = -1.0f;
IntersectLinePlane(lineStart, lineDirection, _plane.Origin(), _plane.Normal(), intersectionDistance);
if (intersectionDistance == -1.0f) {
return;
}
const glm::mat4& planeTransform = _plane.Transform();
glm::vec2 intersectionPlane = glm::inverse(planeTransform) * (glm::vec4(lineStart + lineDirection * intersectionDistance, 1.0f));
glm::vec2 directionPlane = intersectionPlane - _mousePositionPlane;
float dragAbsDistance = abs(glm::distance(glm::vec2(0.0f), directionPlane));
if (dragAbsDistance >= MIN_DRAG_MAGNITUDE) {
glm::vec3 mousePosition = planeTransform * glm::vec4(_mousePositionPlane, 0.0f, 1.0f);
glm::vec3 direction = glm::inverse(planeTransform) * glm::vec4(directionPlane, 0.0f, 0.0f);
direction = glm::normalize(direction);
glm::vec3 axisSingle = glm::cross(_plane.Normal(), direction);
axisSingle = orthogonalise(axisSingle);
float projection = glm::dot(mousePosition, axisSingle);
int index = floor((projection + 0.5f));
glm::ivec3 axis = orthogonalise(axisSingle);
_cube->UndoTransformTemp();
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<float>();
const float quarterRotation = rotations * 0.25f;
void SceneInterface::OnDragStop() {
// Undo any temporarilies
_cube->UndoTransformTemp();
if (_spinDelta < 0.001f)
return;
// 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(Action* action) {
currentAction = action;
action->Invoke();
}
void SceneInterface::Render(float aspectRatio) {
if (_camera == nullptr)
return;
DefaultUniform uniform;
uniform.view = _camera->View();
uniform.projection = _camera->Projection();
for(Entity* entity : _entities) {
uniform.model = entity->LocalToWorld();
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;
}
void SceneInterface::ClearResources() {
}