diff --git a/Common/CAABox.cpp b/Common/CAABox.cpp index 54438ba3..24311c9f 100644 --- a/Common/CAABox.cpp +++ b/Common/CAABox.cpp @@ -34,7 +34,7 @@ CVector3f CAABox::Center() const return (mMax - ((mMax - mMin) * 0.5f)); } -CVector3f CAABox::GetSize() const +CVector3f CAABox::Size() const { return (mMax - mMin); } @@ -49,14 +49,24 @@ CVector3f CAABox::Max() const return mMax; } +void CAABox::SetMin(const CVector3f& min) +{ + mMin = min; +} + +void CAABox::SetMax(const CVector3f& max) +{ + mMax = max; +} + bool CAABox::IsNull() const { - return (GetSize() == CVector3f::skZero); + return (Size() == CVector3f::skZero); } bool CAABox::IsInfinite() const { - return (GetSize() == CVector3f::skInfinite); + return (Size() == CVector3f::skInfinite); } void CAABox::ExpandBounds(const CVector3f& vtx) @@ -76,6 +86,13 @@ void CAABox::ExpandBounds(const CAABox& AABox) ExpandBounds(AABox.mMax); } +void CAABox::ExpandBy(const CVector3f& amount) +{ + CVector3f halfAmount = amount / 2.f; + mMin -= halfAmount; + mMax += halfAmount; +} + CAABox CAABox::Transformed(const CTransform4f& transform) const { CAABox AABox; diff --git a/Common/CAABox.h b/Common/CAABox.h index 72d7c2d2..f64370b1 100644 --- a/Common/CAABox.h +++ b/Common/CAABox.h @@ -19,14 +19,17 @@ public: CAABox(CInputStream& input); void Write(COutputStream& Output); CVector3f Center() const; - CVector3f GetSize() const; + CVector3f Size() const; CVector3f Min() const; CVector3f Max() const; + void SetMin(const CVector3f& min); + void SetMax(const CVector3f& max); bool IsNull() const; bool IsInfinite() const; void ExpandBounds(const CVector3f& vtx); void ExpandBounds(const CAABox& AABox); + void ExpandBy(const CVector3f& amount); CAABox Transformed(const CTransform4f& transform) const; bool IsPointInBox(const CVector3f& Point) const; diff --git a/Common/CVector3f.cpp b/Common/CVector3f.cpp index 8104000f..2a9c6aaf 100644 --- a/Common/CVector3f.cpp +++ b/Common/CVector3f.cpp @@ -60,7 +60,7 @@ float CVector3f::Magnitude() const float CVector3f::SquaredMagnitude() const { - return (powf(x,2) + powf(y,2) + powf(z,2)); + return Dot(*this); } CVector3f CVector3f::Normalized() const diff --git a/Common/Math.cpp b/Common/Math.cpp index a1ceeafc..563cc434 100644 --- a/Common/Math.cpp +++ b/Common/Math.cpp @@ -144,6 +144,69 @@ std::pair RayBoxIntersection(const CRay& Ray, const CAABox& Box) return std::pair(Hit, lowt); } +std::pair RayLineIntersection(const CRay& ray, const CVector3f& pointA, + const CVector3f& pointB, float threshold) +{ + // http://geomalgorithms.com/a07-_distance.html + // http://www.gamedev.net/topic/589705-rayline-intersection-in-3d/ + CVector3f u = ray.Direction(); + CVector3f v = pointB - pointA; + CVector3f w = ray.Origin() - pointA; + float a = u.Dot(u); + float b = u.Dot(v); + float c = v.Dot(v); + float d = u.Dot(w); + float e = v.Dot(w); + float D = a * c - b * b; + float sc, sN, sD = D; + float tc, tN, tD = D; + + if (D < FLT_EPSILON) { + sN = 0.f; + sD = 1.f; + tN = e; + tD = c; + } + else { + sN = b * e - c * d; + tN = a * e - b * d; + + if (sN < 0.f) { + sN = 0.f; + tN = e; + tD = c; + } + } + + if (tN < 0.f) { + tN = 0.f; + + if (-d < 0.f) + sN = 0.f; + else { + sN = -d; + sD = a; + } + } + else if (tN > tD) { + tN = tD; + + if (-d + b < 0.f) + sN = 0.f; + else { + sN = -d + b; + sD = a; + } + } + + sc = (fabs(sN) < FLT_EPSILON ? 0.f : sN / sD); + tc = (fabs(tN) < FLT_EPSILON ? 0.f : tN / tD); + + CVector3f dP = w + (u * sc) - (v * tc); + bool hit = (dP.Magnitude() <= threshold); + return std::pair(hit, sc); +} + std::pair RayTriangleIntersection(const CRay& Ray, const CVector3f& vtxA, const CVector3f& vtxB, const CVector3f& vtxC, bool AllowBackfaces) diff --git a/Common/Math.h b/Common/Math.h index 98ccd478..2f83ca53 100644 --- a/Common/Math.h +++ b/Common/Math.h @@ -16,6 +16,9 @@ float Distance(const CVector3f& A, const CVector3f& B); std::pair RayBoxIntersection(const CRay& Ray, const CAABox& Box); +std::pair RayLineIntersection(const CRay& ray, const CVector3f& pointA, + const CVector3f& pointB, float threshold = 0.02f); + std::pair RayTriangleIntersection(const CRay& Ray, const CVector3f& PointA, const CVector3f& PointB, const CVector3f& PointC, bool AllowBackfaces = false); diff --git a/Core/CDrawUtil.cpp b/Core/CDrawUtil.cpp index fb902448..c4619888 100644 --- a/Core/CDrawUtil.cpp +++ b/Core/CDrawUtil.cpp @@ -151,7 +151,7 @@ void CDrawUtil::DrawWireCube(const CAABox& kAABox, const CColor& kColor) // Calculate model matrix CTransform4f Transform; - Transform.Scale(kAABox.GetSize()); + Transform.Scale(kAABox.Size()); Transform.Translate(kAABox.Center()); CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f(); CGraphics::UpdateMVPBlock(); diff --git a/Resource/model/SSurface.cpp b/Resource/model/SSurface.cpp index 563e1040..cd0a9ab3 100644 --- a/Resource/model/SSurface.cpp +++ b/Resource/model/SSurface.cpp @@ -3,9 +3,8 @@ #include #include -std::pair SSurface::IntersectsRay(const CRay& Ray, const CTransform4f& Transform) +std::pair SSurface::IntersectsRay(const CRay& Ray, float LineThreshold) { - //CDrawUtil::DrawWireCube(AABox.Transformed(Transform), CColor::skRed); bool Hit = false; float HitDist; @@ -14,6 +13,7 @@ std::pair SSurface::IntersectsRay(const CRay& Ray, const CTransform4 SPrimitive *pPrim = &Primitives[iPrim]; u32 NumVerts = pPrim->Vertices.size(); + // Triangles if ((pPrim->Type == eGX_Triangles) || (pPrim->Type == eGX_TriangleFan) || (pPrim->Type == eGX_TriangleStrip)) { u32 NumTris; @@ -23,14 +23,6 @@ std::pair SSurface::IntersectsRay(const CRay& Ray, const CTransform4 else NumTris = NumVerts - 2; - CColor LineColor; - if (pPrim->Type == eGX_Triangles) - LineColor = CColor::skRed; - else if (pPrim->Type == eGX_TriangleStrip) - LineColor = CColor::skYellow; - else if (pPrim->Type == eGX_TriangleFan) - LineColor = CColor::skGreen; - for (u32 iTri = 0; iTri < NumTris; iTri++) { CVector3f vtxA, vtxB, vtxC; @@ -82,7 +74,38 @@ std::pair SSurface::IntersectsRay(const CRay& Ray, const CTransform4 } } - // todo: Intersection tests for line primitives + // Lines + if ((pPrim->Type == eGX_Lines) || (pPrim->Type == eGX_LineStrip)) + { + u32 NumLines; + + if (pPrim->Type == eGX_Lines) + NumLines = NumVerts / 2; + else + NumLines = NumVerts - 1; + + for (u32 iLine = 0; iLine < NumLines; iLine++) + { + CVector3f vtxA, vtxB; + + // Get the two vertices that make up the current line + u32 index = (pPrim->Type == eGX_Lines ? iLine * 2 : iLine); + vtxA = pPrim->Vertices[index].Position; + vtxB = pPrim->Vertices[index+1].Position; + + // Intersection test + std::pair result = Math::RayLineIntersection(Ray, vtxA, vtxB, LineThreshold); + + if (result.first) + { + if ((!Hit) || (result.second < HitDist)) + { + Hit = true; + HitDist = result.second; + } + } + } + } } return std::pair(Hit, HitDist); diff --git a/Resource/model/SSurface.h b/Resource/model/SSurface.h index 6a704600..d04424cf 100644 --- a/Resource/model/SSurface.h +++ b/Resource/model/SSurface.h @@ -33,7 +33,7 @@ struct SSurface TriangleCount = 0; } - std::pair IntersectsRay(const CRay& Ray, const CTransform4f& Transform = CTransform4f::skIdentity); + std::pair IntersectsRay(const CRay& Ray, float LineThreshold = 0.02f); }; #endif // SSURFACE_H diff --git a/Scene/CModelNode.cpp b/Scene/CModelNode.cpp index 536e1bc2..15b60629 100644 --- a/Scene/CModelNode.cpp +++ b/Scene/CModelNode.cpp @@ -112,7 +112,7 @@ SRayIntersection CModelNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID) out.AssetIndex = AssetID; CRay TransformedRay = Ray.Transformed(Transform().Inverse()); - std::pair Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, Transform()); + std::pair Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay); if (Result.first) { diff --git a/Scene/CScriptNode.cpp b/Scene/CScriptNode.cpp index c229a161..c075d6ec 100644 --- a/Scene/CScriptNode.cpp +++ b/Scene/CScriptNode.cpp @@ -213,7 +213,7 @@ SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID) CRay TransformedRay = Ray.Transformed(Transform().Inverse()); CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel()); - std::pair Result = pModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, Transform()); + std::pair Result = pModel->GetSurface(AssetID)->IntersectsRay(TransformedRay); if (Result.first) { @@ -267,8 +267,8 @@ void CScriptNode::GeneratePosition() CScriptNode *pNode = mpScene->ScriptNodeByID(link.ObjectID); pNode->GeneratePosition(); mPosition = pNode->GetAbsolutePosition(); - mPosition.z += (pNode->AABox().GetSize().z / 2.f); - mPosition.z += (AABox().GetSize().z / 2.f); + mPosition.z += (pNode->AABox().Size().z / 2.f); + mPosition.z += (AABox().Size().z / 2.f); mPosition.z += 2.f; } diff --git a/Scene/CStaticNode.cpp b/Scene/CStaticNode.cpp index e0675b8f..04bb6e2a 100644 --- a/Scene/CStaticNode.cpp +++ b/Scene/CStaticNode.cpp @@ -95,7 +95,7 @@ SRayIntersection CStaticNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID) out.AssetIndex = AssetID; CRay TransformedRay = Ray.Transformed(Transform().Inverse()); - std::pair Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, Transform()); + std::pair Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay); if (Result.first) { diff --git a/UI/CGizmo.cpp b/UI/CGizmo.cpp index 4f8f5e9a..c94bc07d 100644 --- a/UI/CGizmo.cpp +++ b/UI/CGizmo.cpp @@ -1,4 +1,5 @@ #include "CGizmo.h" +#include #include #include @@ -6,7 +7,7 @@ CGizmo::CGizmo() { LoadModels(); - mMode = eRotate; + SetMode(eTranslate); mSelectedAxes = eNone; mGizmoSize = 1.f; mCameraDist = 0.f; @@ -31,29 +32,15 @@ void CGizmo::AddToRenderer(CRenderer *pRenderer) // Transform is updated every frame even if the user doesn't modify the gizmo // in order to account for scale changes based on camera distance UpdateTransform(); - - // Determine which SModelPart array to use - SModelPart *pParts; - u32 numParts; - - if (mMode == eTranslate) { - pParts = smTranslateModels; - numParts = 6; - } else if (mMode == eRotate) { - pParts = smRotateModels; - numParts = 4; - } else if (mMode == eScale) { - pParts = smScaleModels; - numParts = 7; - } + SModelPart *pPart = mpCurrentParts; // Add all parts to renderer - for (u32 iPart = 0; iPart < numParts; iPart++) + for (u32 iPart = 0; iPart < mNumCurrentParts; iPart++) { - CModel *pModel = pParts->pModel; + CModel *pModel = pPart->pModel; // Determine whether to use the mat set for regular (0) or highlight (1) - bool isHighlighted = (mSelectedAxes & pParts->modelAxes) == pParts->modelAxes; + bool isHighlighted = (mSelectedAxes & pPart->modelAxes) == pPart->modelAxes; u32 setID = (isHighlighted ? 1 : 0); // Add to renderer... @@ -62,7 +49,7 @@ void CGizmo::AddToRenderer(CRenderer *pRenderer) else pRenderer->AddOpaqueMesh(this, iPart, pModel->AABox().Transformed(mTransform), eDrawAsset); - pParts++; + pPart++; } } @@ -72,26 +59,13 @@ void CGizmo::DrawAsset(ERenderOptions options, u32 asset) CGraphics::UpdateMVPBlock(); // Determine which SModelPart array to use - SModelPart *pParts; - u32 numParts; - - if (mMode == eTranslate) { - pParts = smTranslateModels; - numParts = 6; - } else if (mMode == eRotate) { - pParts = smRotateModels; - numParts = 4; - } else if (mMode == eScale) { - pParts = smScaleModels; - numParts = 7; - } - - if (asset >= numParts) return; + if (asset >= mNumCurrentParts) return; + SModelPart *pPart = mpCurrentParts; // Draw model - bool isHighlighted = (mSelectedAxes & pParts[asset].modelAxes) == pParts[asset].modelAxes; + bool isHighlighted = (mSelectedAxes & pPart[asset].modelAxes) == pPart[asset].modelAxes; u32 setID = (isHighlighted ? 1 : 0); - pParts[asset].pModel->Draw((ERenderOptions) 0, setID); + pPart[asset].pModel->Draw((ERenderOptions) 0, setID); } void CGizmo::DrawRotationOutline() @@ -135,6 +109,80 @@ void CGizmo::UpdateForCamera(const CCamera &camera) mBillboardRotation = CQuaternion::FromAxisAngle(angle, axis); } +bool CGizmo::IntersectsRay(const CRay &ray) +{ + // todo: fix raycasting for rotate gizmo; currently it can hit the invisible back side of the model + CRay localRay = ray.Transformed(mTransform.Inverse()); + float threshold = 0.02f * mGizmoSize * mCameraDist; + + // Do raycast on each model + SModelPart *pPart = mpCurrentParts; + + struct SResult { + SModelPart *pPart; + float dist; + }; + std::list results; + + for (u32 iPart = 0; iPart < mNumCurrentParts; iPart++) + { + if (!pPart->enableRayCast) continue; + CModel *pModel = pPart->pModel; + + // Ray/Model AABox test - allow buffer room because lines are small + CAABox AABox = pModel->AABox(); + AABox.ExpandBy(CVector3f::skOne); + bool modelBoxCheck = Math::RayBoxIntersection(localRay, AABox).first; + + if (modelBoxCheck) + { + bool hit = false; + float dist; + + for (u32 iSurf = 0; iSurf < pModel->GetSurfaceCount(); iSurf++) + { + // Skip surface/box check + SSurface *pSurf = pModel->GetSurface(iSurf); + std::pair surfCheck = pSurf->IntersectsRay(localRay, 0.05f); + + if (surfCheck.first) + { + if ((!hit) || (surfCheck.second < dist)) + dist = surfCheck.second; + + hit = true; + } + } + + if (hit) + { + SResult result; + result.pPart = pPart; + result.dist = dist; + results.push_back(result); + } + } + + pPart++; + } + + // Results list empty = no hits + if (results.empty()) + { + mSelectedAxes = eNone; + return false; + } + + // Otherwise, we have at least one hit - sort results and set selected axes + results.sort([](const SResult& a, SResult& b) -> bool + { + return (a.dist < b.dist); + }); + + mSelectedAxes = results.front().pPart->modelAxes; + return true; +} + CGizmo::EGizmoMode CGizmo::Mode() { return mMode; @@ -143,6 +191,24 @@ CGizmo::EGizmoMode CGizmo::Mode() void CGizmo::SetMode(EGizmoMode mode) { mMode = mode; + + switch (mode) + { + case eTranslate: + mpCurrentParts = smTranslateModels; + mNumCurrentParts = 9; + break; + + case eRotate: + mpCurrentParts = smRotateModels; + mNumCurrentParts = 4; + break; + + case eScale: + mpCurrentParts = smScaleModels; + mNumCurrentParts = 10; + break; + } } void CGizmo::SetPosition(const CVector3f& position) @@ -150,31 +216,42 @@ void CGizmo::SetPosition(const CVector3f& position) mPosition = position; } +void CGizmo::ResetSelectedAxes() +{ + mSelectedAxes = eNone; +} + // ************ PRIVATE STATIC ************ void CGizmo::LoadModels() { if (!smModelsLoaded) { - smTranslateModels[CGIZMO_TRANSLATE_X] = SModelPart(eX, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoX.CMDL")); - smTranslateModels[CGIZMO_TRANSLATE_Y] = SModelPart(eY, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoY.CMDL")); - smTranslateModels[CGIZMO_TRANSLATE_Z] = SModelPart(eZ, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoZ.CMDL")); - smTranslateModels[CGIZMO_TRANSLATE_XY] = SModelPart(eXY, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoXY.CMDL")); - smTranslateModels[CGIZMO_TRANSLATE_XZ] = SModelPart(eXZ, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoXZ.CMDL")); - smTranslateModels[CGIZMO_TRANSLATE_YZ] = SModelPart(eYZ, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoYZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_X] = SModelPart(eX, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateX.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_Y] = SModelPart(eY, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateY.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_Z] = SModelPart(eZ, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_LINES_XY] = SModelPart(eXY, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesXY.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_LINES_XZ] = SModelPart(eXZ, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesXZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_LINES_YZ] = SModelPart(eYZ, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesYZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_POLY_XY] = SModelPart(eXY, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyXY.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_POLY_XZ] = SModelPart(eXZ, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyXZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_POLY_YZ] = SModelPart(eYZ, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyYZ.CMDL")); - smRotateModels[CGIZMO_ROTATE_X] = SModelPart(eX, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoX.CMDL")); - smRotateModels[CGIZMO_ROTATE_Y] = SModelPart(eY, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoY.CMDL")); - smRotateModels[CGIZMO_ROTATE_Z] = SModelPart(eZ, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoZ.CMDL")); - smRotateModels[CGIZMO_ROTATE_XYZ] = SModelPart(eXYZ, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoXYZ.CMDL")); - smRotateClipOutline = SModelPart(eNone, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoClipOutline.CMDL")); + smRotateModels[CGIZMO_ROTATE_X] = SModelPart(eX, true, (CModel*) gResCache.GetResource("../resources/editor/RotateX.CMDL")); + smRotateModels[CGIZMO_ROTATE_Y] = SModelPart(eY, true, (CModel*) gResCache.GetResource("../resources/editor/RotateY.CMDL")); + smRotateModels[CGIZMO_ROTATE_Z] = SModelPart(eZ, true, (CModel*) gResCache.GetResource("../resources/editor/RotateZ.CMDL")); + smRotateModels[CGIZMO_ROTATE_XYZ] = SModelPart(eXYZ, false, (CModel*) gResCache.GetResource("../resources/editor/RotateXYZ.CMDL")); + smRotateClipOutline = SModelPart(eNone, false, (CModel*) gResCache.GetResource("../resources/editor/RotateClipOutline.CMDL")); - smScaleModels[CGIZMO_SCALE_X] = SModelPart(eX, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoX.CMDL")); - smScaleModels[CGIZMO_SCALE_Y] = SModelPart(eY, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoY.CMDL")); - smScaleModels[CGIZMO_SCALE_Z] = SModelPart(eZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoZ.CMDL")); - smScaleModels[CGIZMO_SCALE_XY] = SModelPart(eXY, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoXY.CMDL")); - smScaleModels[CGIZMO_SCALE_XZ] = SModelPart(eXZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoXZ.CMDL")); - smScaleModels[CGIZMO_SCALE_YZ] = SModelPart(eYZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoYZ.CMDL")); - smScaleModels[CGIZMO_SCALE_XYZ] = SModelPart(eXYZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoXYZ.CMDL")); + smScaleModels[CGIZMO_SCALE_X] = SModelPart(eX, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleX.CMDL")); + smScaleModels[CGIZMO_SCALE_Y] = SModelPart(eY, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleY.CMDL")); + smScaleModels[CGIZMO_SCALE_Z] = SModelPart(eZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleZ.CMDL")); + smScaleModels[CGIZMO_SCALE_LINES_XY] = SModelPart(eXY, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesXY.CMDL")); + smScaleModels[CGIZMO_SCALE_LINES_XZ] = SModelPart(eXZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesXZ.CMDL")); + smScaleModels[CGIZMO_SCALE_LINES_YZ] = SModelPart(eYZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesYZ.CMDL")); + smScaleModels[CGIZMO_SCALE_POLY_XY] = SModelPart(eXY, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyXY.CMDL")); + smScaleModels[CGIZMO_SCALE_POLY_XZ] = SModelPart(eXZ, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyXZ.CMDL")); + smScaleModels[CGIZMO_SCALE_POLY_YZ] = SModelPart(eYZ, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyYZ.CMDL")); + smScaleModels[CGIZMO_SCALE_XYZ] = SModelPart(eXYZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleXYZ.CMDL")); smModelsLoaded = true; } @@ -215,7 +292,7 @@ void CGizmo::UpdateTransform() // ************ STATIC MEMBER INITIALIZATION ************ bool CGizmo::smModelsLoaded = false; -CGizmo::SModelPart CGizmo::smTranslateModels[6]; +CGizmo::SModelPart CGizmo::smTranslateModels[9]; CGizmo::SModelPart CGizmo::smRotateModels[4]; -CGizmo::SModelPart CGizmo::smScaleModels[7]; +CGizmo::SModelPart CGizmo::smScaleModels[10]; CGizmo::SModelPart CGizmo::smRotateClipOutline; diff --git a/UI/CGizmo.h b/UI/CGizmo.h index a1acd33d..9d7348fe 100644 --- a/UI/CGizmo.h +++ b/UI/CGizmo.h @@ -12,9 +12,12 @@ #define CGIZMO_TRANSLATE_X 0 #define CGIZMO_TRANSLATE_Y 1 #define CGIZMO_TRANSLATE_Z 2 -#define CGIZMO_TRANSLATE_XY 3 -#define CGIZMO_TRANSLATE_XZ 4 -#define CGIZMO_TRANSLATE_YZ 5 +#define CGIZMO_TRANSLATE_LINES_XY 3 +#define CGIZMO_TRANSLATE_LINES_XZ 4 +#define CGIZMO_TRANSLATE_LINES_YZ 5 +#define CGIZMO_TRANSLATE_POLY_XY 6 +#define CGIZMO_TRANSLATE_POLY_XZ 7 +#define CGIZMO_TRANSLATE_POLY_YZ 8 #define CGIZMO_ROTATE_X 0 #define CGIZMO_ROTATE_Y 1 #define CGIZMO_ROTATE_Z 2 @@ -22,10 +25,13 @@ #define CGIZMO_SCALE_X 0 #define CGIZMO_SCALE_Y 1 #define CGIZMO_SCALE_Z 2 -#define CGIZMO_SCALE_XY 3 -#define CGIZMO_SCALE_XZ 4 -#define CGIZMO_SCALE_YZ 5 -#define CGIZMO_SCALE_XYZ 6 +#define CGIZMO_SCALE_LINES_XY 3 +#define CGIZMO_SCALE_LINES_XZ 4 +#define CGIZMO_SCALE_LINES_YZ 5 +#define CGIZMO_SCALE_POLY_XY 6 +#define CGIZMO_SCALE_POLY_XZ 7 +#define CGIZMO_SCALE_POLY_YZ 8 +#define CGIZMO_SCALE_XYZ 9 class CGizmo : public IRenderable { @@ -66,27 +72,31 @@ private: bool mFlipScaleY; bool mFlipScaleZ; - // Static struct SModelPart { EGizmoAxes modelAxes; + bool enableRayCast; CModel *pModel; CToken modelToken; SModelPart() {} - SModelPart(EGizmoAxes axes, CModel *_pModel) : - modelAxes(axes), pModel(_pModel), modelToken(_pModel) {} + SModelPart(EGizmoAxes axes, bool rayCastOn, CModel *_pModel) : + modelAxes(axes), enableRayCast(rayCastOn), pModel(_pModel), modelToken(_pModel) {} }; + SModelPart *mpCurrentParts; + u32 mNumCurrentParts; + // Static static bool smModelsLoaded; - static SModelPart smTranslateModels[6]; + static SModelPart smTranslateModels[9]; static SModelPart smRotateModels[4]; - static SModelPart smScaleModels[7]; + static SModelPart smScaleModels[10]; static SModelPart smRotateClipOutline; public: CGizmo(); ~CGizmo(); + void AddToRenderer(CRenderer *pRenderer); void DrawAsset(ERenderOptions options, u32 asset); void DrawRotationOutline(); @@ -99,6 +109,7 @@ public: EGizmoMode Mode(); void SetMode(EGizmoMode mode); void SetPosition(const CVector3f& position); + void ResetSelectedAxes(); // Protected protected: diff --git a/UI/CWorldEditor.cpp b/UI/CWorldEditor.cpp index 9abb5b6a..f8fe2188 100644 --- a/UI/CWorldEditor.cpp +++ b/UI/CWorldEditor.cpp @@ -135,6 +135,10 @@ void CWorldEditor::ViewportRayCast(CRay Ray) { if (!ui->MainViewport->IsMouseInputActive()) { + // Gizmo ray check + mGizmoHovering = mGizmo.IntersectsRay(Ray); + + // Scene ray check SRayIntersection Result = mpSceneManager->SceneRayCast(Ray); if (Result.Hit) @@ -151,7 +155,10 @@ void CWorldEditor::ViewportRayCast(CRay Ray) ResetHover(); } else + { + mGizmo.ResetSelectedAxes(); ResetHover(); + } } CRenderer* CWorldEditor::Renderer() @@ -347,7 +354,9 @@ void CWorldEditor::UpdateCursor() { if (ui->MainViewport->IsCursorVisible()) { - if ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode)) + if (mGizmoHovering) + ui->MainViewport->SetCursorState(Qt::SizeAllCursor); + else if ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode)) ui->MainViewport->SetCursorState(Qt::PointingHandCursor); else ui->MainViewport->SetCursorState(Qt::ArrowCursor); @@ -359,7 +368,7 @@ void CWorldEditor::UpdateStatusBar() // Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag. QString StatusText = ""; - if (mpHoverNode) + if (!mGizmoHovering && mpHoverNode) { if (mpHoverNode->NodeType() != eStaticNode) StatusText = QString::fromStdString(mpHoverNode->Name()); diff --git a/UI/CWorldEditor.h b/UI/CWorldEditor.h index 4b26b0db..858107c3 100644 --- a/UI/CWorldEditor.h +++ b/UI/CWorldEditor.h @@ -33,6 +33,7 @@ class CWorldEditor : public QMainWindow CTimer mFrameTimer; bool mDrawSky; bool mShowGizmo; + bool mGizmoHovering; CVector3f mHoverPoint; CSceneNode *mpHoverNode;