diff --git a/core/src/datatypes/cframe.cpp b/core/src/datatypes/cframe.cpp index d1fe67f..805f4a2 100644 --- a/core/src/datatypes/cframe.cpp +++ b/core/src/datatypes/cframe.cpp @@ -64,7 +64,11 @@ const Data::String Data::CFrame::ToString() const { } Data::CFrame Data::CFrame::pointToward(Vector3 position, Vector3 toward) { - return Data::CFrame(position, position + toward, (abs(glm::dot((glm::vec3)toward, glm::vec3(0, 1, 0))) > 0.999) ? glm::vec3(0, 0, 1) : glm::vec3(0, 1, 0)); + return Data::CFrame(position, position + toward, (abs(toward.Dot(Vector3(0, 1, 0))) > 0.999) ? Vector3(0, 0, 1) : Vector3(0, 1, 0)); +} + +Data::CFrame Data::CFrame::pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right) { + return Data::CFrame(position, position + toward, (abs(toward.Dot(up)) > 0.999) ? right : up); } Data::CFrame::operator glm::mat4() const { diff --git a/core/src/datatypes/cframe.h b/core/src/datatypes/cframe.h index a4aca7f..4871707 100644 --- a/core/src/datatypes/cframe.h +++ b/core/src/datatypes/cframe.h @@ -30,6 +30,11 @@ namespace Data { // Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant static CFrame pointToward(Vector3 position, Vector3 toward); + // Creates a cframe looking at position + toward, whilst aligning its up to up. + // If up and toward are approximately linearly dependent (their absolute dot product > 0.999), + // then the right is used instead + // Up and right must NOT be linearly dependent + static CFrame pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right); DEF_DATA_PROP static const CFrame IDENTITY; static const CFrame YToZ; diff --git a/core/src/handles.cpp b/core/src/handles.cpp index 1f77ec1..e0ad9df 100644 --- a/core/src/handles.cpp +++ b/core/src/handles.cpp @@ -2,6 +2,7 @@ #include "common.h" #include "datatypes/cframe.h" #include "datatypes/vector.h" +#include "math_helper.h" #include #include #include @@ -33,29 +34,6 @@ std::shared_ptr getHandleAdornee() { return {}; } -CFrame getCFrameOfHandle(HandleFace face) { - auto adornee = getHandleAdornee(); - if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0)); - - CFrame localFrame = editorToolHandles.worldMode ? CFrame::IDENTITY + adornee->position() : adornee->cframe; - - Vector3 handleNormal = face.normal; - if (editorToolHandles.nixAxes) - handleNormal = XYZToZXY * face.normal; - - // We don't want this to align with local * face.normal, or else we have problems. - glm::vec3 upAxis(0, 0, 1); - if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * handleNormal), upAxis)) > 0.9999f) - upAxis = glm::vec3(0, 1, 0); - - glm::vec3 partSize = editorToolHandles.handlesType == HandlesType::RotateHandles ? glm::vec3(glm::max(adornee->size.x, adornee->size.y, adornee->size.z)) : adornee->size; - Vector3 handleOffset = editorToolHandles.worldMode ? ((Vector3::ONE * 2.f) + adornee->GetAABB() * 0.5f) : Vector3(2.f + partSize * 0.5f); - Vector3 handlePos = localFrame * (handleOffset * handleNormal); - CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis); - - return cframe; -} - CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) { auto adornee = getHandleAdornee(); if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0)); @@ -73,7 +51,7 @@ CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) { std::optional raycastHandle(rp3d::Ray ray) { for (HandleFace face : HandleFace::Faces) { - CFrame cframe = getCFrameOfHandle(face); + CFrame cframe = getHandleCFrame(face); // Implement manual detection via boxes instead of... this shit // This code also hardly works, and is not good at all... Hooo nope. rp3d::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position()); @@ -95,4 +73,64 @@ Vector3 handleSize(HandleFace face) { if (editorToolHandles.handlesType == HandlesType::MoveHandles) return glm::vec3(0.5f, 0.5f, 2.f); return glm::vec3(1,1,1); +} + +static int getAABBOfSelection(glm::vec3& pos, glm::vec3& size, glm::vec3& min, glm::vec3& max) { + int count = 0; + for (std::weak_ptr inst : getSelection()) { + if (inst.expired() || !inst.lock()->IsA()) continue; + std::shared_ptr part = inst.lock()->CastTo().expect(); + + if (count == 0) + min = part->position(), max = part->position(); + count++; + + Vector3 aabbSize = part->GetAABB(); + expandAABB(min, max, part->position() - aabbSize / 2.f); + expandAABB(min, max, part->position() + aabbSize / 2.f); + } + + getAABBCoords(pos, size, min, max); + + return count; +} + +static std::shared_ptr getFirstSelectedPart() { + for (std::weak_ptr inst : getSelection()) { + if (inst.expired() || !inst.lock()->IsA()) continue; + + return inst.lock()->CastTo().expect(); + } + + return {}; +} + +CFrame getLocalHandleCFrame(HandleFace face) { + glm::vec3 _boxPos, boxSize, _boxMin, _boxMax; + int count = getAABBOfSelection(_boxPos, boxSize, _boxMin, _boxMax); + + Vector3 size; + if (count == 1 && !editorToolHandles.worldMode) + size = getFirstSelectedPart()->size; + else + size = boxSize; + + // Since rotation displays rings, all handles must be the same distance from origin in order for the + // rings to be circular + if (editorToolHandles.handlesType == HandlesType::RotateHandles) + size = Vector3::ONE * fmax(fmax(size.X(), size.Y()), size.Z()); + + CFrame cframe = CFrame::pointToward(face.normal * ((glm::vec3)size * 0.5f + 2.f), -face.normal); + return cframe; +} + +CFrame getHandleCFrame(HandleFace face) { + glm::vec3 boxPos, boxSize, _boxMin, _boxMax; + int count = getAABBOfSelection(boxPos, boxSize, _boxMin, _boxMax); + + if (count == 1 && !editorToolHandles.worldMode) { + auto part = getFirstSelectedPart(); + return part->cframe * getLocalHandleCFrame(face); + } else + return getLocalHandleCFrame(face) + boxPos; } \ No newline at end of file diff --git a/core/src/handles.h b/core/src/handles.h index 7805922..eaaf83e 100644 --- a/core/src/handles.h +++ b/core/src/handles.h @@ -37,7 +37,10 @@ struct Handles { }; std::shared_ptr getHandleAdornee(); -CFrame getCFrameOfHandle(HandleFace face); +CFrame getHandleCFrame(HandleFace face); CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos); Vector3 handleSize(HandleFace face); -std::optional raycastHandle(rp3d::Ray ray); \ No newline at end of file +std::optional raycastHandle(rp3d::Ray ray); + +// Gets the cframe of the handle local to the center of the selected objects +CFrame getLocalHandleCFrame(HandleFace face); diff --git a/core/src/math_helper.cpp b/core/src/math_helper.cpp index 384d95e..6999d00 100644 --- a/core/src/math_helper.cpp +++ b/core/src/math_helper.cpp @@ -117,4 +117,9 @@ void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, in min = glm::vec3(glm::min(min.x, points[i].x), glm::min(min.y, points[i].y), glm::min(min.z, points[i].z)); max = glm::vec3(glm::max(max.x, points[i].x), glm::max(max.y, points[i].y), glm::max(max.z, points[i].z)); } +} + +void getAABBCoords(glm::vec3 &pos, glm::vec3 &size, glm::vec3 min, glm::vec3 max) { + pos = (max + min) / 2.f; + size = (max - min); } \ No newline at end of file diff --git a/core/src/math_helper.h b/core/src/math_helper.h index 7f8f6cc..5d870de 100644 --- a/core/src/math_helper.h +++ b/core/src/math_helper.h @@ -5,4 +5,5 @@ void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt); void expandAABB(glm::vec3& min, glm::vec3& max, glm::vec3 point); -void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, int count); \ No newline at end of file +void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, int count); +void getAABBCoords(glm::vec3& pos, glm::vec3& size, glm::vec3 min, glm::vec3 max); \ No newline at end of file diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp index 77e168a..2db2d48 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -266,6 +266,8 @@ void renderSkyBox() { glDrawArrays(GL_TRIANGLES, 0, 36); } +static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1)); + void renderHandles() { if (!editorToolHandles.active) return; @@ -293,11 +295,11 @@ void renderHandles() { handleShader->set("viewPos", camera.cameraPos); for (auto face : HandleFace::Faces) { - glm::mat4 model = getCFrameOfHandle(face); + glm::mat4 model = getHandleCFrame(face); model = glm::scale(model, (glm::vec3)handleSize(face)); handleShader->set("model", model); handleShader->set("material", Material { - .diffuse = glm::abs(face.normal), + .diffuse = editorToolHandles.handlesType == HandlesType::RotateHandles ? (glm::vec3)(XYZToZXY * glm::abs(face.normal)) : glm::abs(face.normal), .specular = glm::vec3(0.5f, 0.5f, 0.5f), .shininess = 16.0f, }); @@ -320,7 +322,7 @@ void renderHandles() { identityShader->set("aColor", glm::vec3(0.f, 1.f, 1.f)); for (auto face : HandleFace::Faces) { - CFrame cframe = getCFrameOfHandle(face); + CFrame cframe = getHandleCFrame(face); glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f); if (screenPos.z < 0) continue; diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index 2444059..9e99781 100755 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -183,7 +183,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); pointDir = glm::normalize(pointDir); - CFrame handleCFrame = getCFrameOfHandle(draggingHandle.value()); + CFrame handleCFrame = getHandleCFrame(draggingHandle.value()); // Current frame. Identity frame if worldMode == true, selected object's frame if worldMode == false CFrame frame = editorToolHandles.worldMode ? CFrame::IDENTITY + part->position() : part->cframe.Rotation(); @@ -283,14 +283,16 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) { if (snappingFactor() > 0) angle = roundf(angle * 4 / PI / snappingFactor()) / 4 * PI * snappingFactor(); + glm::vec3 handleNormal = XYZToZXY * glm::abs(draggingHandle->normal); + // Checks if the rotation axis is facing towards, or away from the camera // If it pointing away from the camera, then we need to invert the angle change - glm::vec4 rotationAxis = projection * view * glm::vec4((glm::vec3)(initialFrame * glm::abs(draggingHandle->normal)), 1.f); + glm::vec4 rotationAxis = projection * view * glm::vec4((glm::vec3)(initialFrame * handleNormal), 1.f); rotationAxis /= rotationAxis.w; glm::vec4 signVec = glm::normalize(rotationAxis - partCenterRaw); float sign = -glm::sign(signVec.z); - glm::vec3 angles = glm::abs(draggingHandle->normal) * sign * glm::vec3(angle); + glm::vec3 angles = handleNormal * sign * glm::vec3(angle); part->cframe = initialFrame * CFrame::FromEulerAnglesXYZ(-angles); diff --git a/editor/placedocument.cpp b/editor/placedocument.cpp index 2c7c629..de1c3b5 100644 --- a/editor/placedocument.cpp +++ b/editor/placedocument.cpp @@ -133,5 +133,5 @@ void PlaceDocument::init() { std::shared_ptr