From 2d25841852f2c903e9fbfb0d2fc30cfac2a883a2 Mon Sep 17 00:00:00 2001 From: MegaMech <7255464+MegaMech@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:39:35 -0600 Subject: [PATCH] Editor Accurate mouse click drag objects --- src/engine/editor/EditorMath.cpp | 101 ++++++++++++++++++++--------- src/engine/editor/EditorMath.h | 11 +++- src/engine/editor/Gizmo.cpp | 42 ++++++------ src/engine/editor/Gizmo.h | 4 ++ src/engine/editor/ObjectPicker.cpp | 59 ++++++++++------- src/engine/editor/ObjectPicker.h | 2 +- src/port/Engine.cpp | 8 +++ src/port/Engine.h | 4 ++ 8 files changed, 151 insertions(+), 80 deletions(-) diff --git a/src/engine/editor/EditorMath.cpp b/src/engine/editor/EditorMath.cpp index 10c067772..42eeb89c9 100644 --- a/src/engine/editor/EditorMath.cpp +++ b/src/engine/editor/EditorMath.cpp @@ -43,10 +43,9 @@ FVector ScreenRayTrace() { Ship::Coords mouse = wnd->GetMousePos(); mouse.x -= gfx_current_game_window_viewport.x; mouse.y -= gfx_current_game_window_viewport.y; - // Get screen dimensions - uint32_t width = OTRGetGameRenderWidth(); - uint32_t height = OTRGetGameRenderHeight(); + uint32_t width = OTRGetGameViewportWidth(); + uint32_t height = OTRGetGameViewportHeight(); // Convert mouse to NDS screen coordinates float x = (2.0f * mouse.x) / width - 1.0f; // Normalized X: -1 to 1 @@ -76,6 +75,7 @@ FVector ScreenRayTrace() { return direction; } } + return FVector(0, 0, 0); } bool QueryCollisionRayActor(Vec3f rayOrigin, Vec3f rayDir, Vec3f actorMin, Vec3f actorMax, float* t) { @@ -207,17 +207,42 @@ void Clear(MtxF* mf) { mf->yw = 0.0f; mf->zw = 0.0f; } -FVector DebugPoss = {0, 0, 0}; -bool IntersectRayTriangle(const Ray& ray, const Triangle& tri, const FVector& objectPos, float& t) { - const float EPSILON = 1e-6f; + +FVector TransformVecByMatrix(const FVector& vec, const float mtx[4][4]) { + FVector result; + result.x = vec.x * mtx[0][0] + vec.y * mtx[1][0] + vec.z * mtx[2][0] + mtx[3][0]; + result.y = vec.x * mtx[0][1] + vec.y * mtx[1][1] + vec.z * mtx[2][1] + mtx[3][1]; + result.z = vec.x * mtx[0][2] + vec.y * mtx[1][2] + vec.z * mtx[2][2] + mtx[3][2]; + return result; +} + +FVector TransformVecDirection(const FVector& dir, const float mtx[4][4]) { + FVector result; + result.x = dir.x * mtx[0][0] + dir.y * mtx[1][0] + dir.z * mtx[2][0]; + result.y = dir.x * mtx[0][1] + dir.y * mtx[1][1] + dir.z * mtx[2][1]; + result.z = dir.x * mtx[0][2] + dir.y * mtx[1][2] + dir.z * mtx[2][2]; + return result; +} + +Ray RayToLocalSpace(MtxF mtx, const Ray& ray) { + MtxF inverse; + + if (Inverse(&mtx, &inverse) != 2) { + FVector localRayOrigin = TransformVecByMatrix(ray.Origin, (float(*)[4])&inverse); + FVector localRayDir = TransformVecDirection(ray.Direction, (float(*)[4])&inverse); + return Ray{localRayOrigin, localRayDir.Normalize()}; + } + return Ray{}; // Fail. Return empty ray +} + +bool IntersectRayTriangle(const Ray& ray, const Triangle& tri, float& t) { + constexpr float EPSILON = 1e-6f; // Adjust the triangle vertices by the object's position FVector v0 = tri.v0; FVector v1 = tri.v1; FVector v2 = tri.v2; - DebugPoss = v0; - FVector edge1 = v1 - v0; FVector edge2 = v2 - v0; FVector h = ray.Direction.Cross(edge2); @@ -243,35 +268,49 @@ bool IntersectRayTriangle(const Ray& ray, const Triangle& tri, const FVector& ob return t > EPSILON; } -// Apply location, rotation, and scale transformations. -FVector TransformPoint(const FVector& point, const FVector& pos, const IRotator& n64Rot, const FVector& scale) { - FVector rot = n64Rot.ToRadians(); +bool IntersectRayTriangleAndTransform(const Ray& ray, FVector pos, const Triangle& tri, float& t) { + constexpr float EPSILON = 1e-6f; - // Apply scale - FVector scaled = FVector(point.x * scale.x, point.y * scale.y, point.z * scale.z); + // Adjust the triangle vertices by the object's position + FVector v0 = tri.v0 + pos; + FVector v1 = tri.v1 + pos; + FVector v2 = tri.v2 + pos; - // Apply rotation (ZXY order, typical in games) - float cz = cos(rot.z), sz = sin(rot.z); - float cx = cos(rot.x), sx = sin(rot.x); - float cy = cos(rot.y), sy = sin(rot.y); + FVector edge1 = v1 - v0; + FVector edge2 = v2 - v0; + FVector h = ray.Direction.Cross(edge2); + float a = edge1.Dot(h); - // Rotate around Z axis - float x1 = scaled.x * cz - scaled.y * sz; - float y1 = scaled.x * sz + scaled.y * cz; - float z1 = scaled.z; + if (std::abs(a) < EPSILON) + return false; // Ray is parallel to triangle - // Rotate around X axis - float y2 = y1 * cx - z1 * sx; - float z2 = y1 * sx + z1 * cx; - float x2 = x1; + float f = 1.0f / a; + FVector s = ray.Origin - v0; + float u = f * s.Dot(h); - // Rotate around Y axis - float x3 = x2 * cy + z2 * sy; - float y3 = y2; - float z3 = -x2 * sy + z2 * cy; + if (u < 0.0f || u > 1.0f) + return false; - // Apply translation - return FVector(x3 + pos.x, y3 + pos.y, z3 + pos.z); + FVector q = s.Cross(edge1); + float v = f * ray.Direction.Dot(q); + + if (v < 0.0f || u + v > 1.0f) + return false; + + t = f * edge2.Dot(q); + return t > EPSILON; +} + +std::optional QueryHandleIntersection(MtxF mtx, Ray ray, Triangle& tri) { + float t; + Ray localRay = RayToLocalSpace(mtx, ray); + if (IntersectRayTriangle(localRay, tri, t)) { + FVector localClickPosition = localRay.Origin + localRay.Direction * t; + FVector worldClickPosition = TransformVecByMatrix(localClickPosition, (float(*)[4])&mtx); + + return worldClickPosition; // Stop checking objects if we selected a Gizmo handle + } + return std::nullopt; } bool IntersectRaySphere(const Ray& ray, const FVector& sphereCenter, float radius, float& t) { diff --git a/src/engine/editor/EditorMath.h b/src/engine/editor/EditorMath.h index 26d5e1497..55ac3c764 100644 --- a/src/engine/editor/EditorMath.h +++ b/src/engine/editor/EditorMath.h @@ -39,9 +39,16 @@ s32 Inverse(MtxF* src, MtxF* dest); void Copy(MtxF* src, MtxF* dest); void Clear(MtxF* mf); -bool IntersectRayTriangle(const Ray& ray, const Triangle& tri, const FVector& objectPos, float& t); +FVector TransformVecByMatrix(const FVector& vec, const float mtx[4][4]); +FVector TransformVecDirection(const FVector& dir, const float mtx[4][4]); +Ray RayToLocalSpace(MtxF mtx, const Ray& ray); +bool IntersectRayTriangle(const Ray& ray, const Triangle& tri, float& t); // Uses local space not global space. +bool IntersectRayTriangleAndTransform(const Ray& ray, FVector pos, const Triangle& tri, float& t); bool IntersectRaySphere(const Ray& ray, const FVector& sphereCenter, float radius, float& t); -FVector TransformPoint(const FVector& point, const FVector& pos, const IRotator& n64Rot, const FVector& scale); +/** + * optional used here so we can check for successful query and return the click position + */ +std::optional QueryHandleIntersection(MtxF mtx, Ray ray, Triangle& tri); void Editor_MatrixIdentity(Mat4 mtx); void Editor_AddMatrix(Mat4 mtx, int32_t flags); diff --git a/src/engine/editor/Gizmo.cpp b/src/engine/editor/Gizmo.cpp index 8aba3cc0e..1b5b7dc5d 100644 --- a/src/engine/editor/Gizmo.cpp +++ b/src/engine/editor/Gizmo.cpp @@ -48,14 +48,14 @@ void Gizmo::Load() { BlueRotateCollision.Pos = &Pos; BlueRotateCollision.Model = (Gfx*)"__OTR__editor/gizmo/rot_handle_blue"; - //GenerateCollisionMesh(&RedCollision, (Gfx*)(handle_Cylinder_mesh), 1); - /*GenerateCollisionMesh(&RedCollision, (Gfx*)LOAD_ASSET_RAW(RedCollision.Model), 0.5f); - GenerateCollisionMesh(&GreenCollision, (Gfx*)LOAD_ASSET_RAW(GreenCollision.Model), 0.5f); - GenerateCollisionMesh(&BlueCollision, (Gfx*)LOAD_ASSET_RAW(BlueCollision.Model), 0.5f); +// GenerateCollisionMesh(&RedCollision, (Gfx*)(handle_Cylinder_mesh), 1); + GenerateCollisionMesh(&RedCollision, (Gfx*)LOAD_ASSET_RAW(RedCollision.Model), 1.0f); + GenerateCollisionMesh(&GreenCollision, (Gfx*)LOAD_ASSET_RAW(GreenCollision.Model), 1.0f); + GenerateCollisionMesh(&BlueCollision, (Gfx*)LOAD_ASSET_RAW(BlueCollision.Model), 1.0f); - GenerateCollisionMesh(&RedRotateCollision, (Gfx*)LOAD_ASSET_RAW(RedRotateCollision.Model), 0.15f); - GenerateCollisionMesh(&GreenRotateCollision, (Gfx*)LOAD_ASSET_RAW(GreenRotateCollision.Model), 0.15f); - GenerateCollisionMesh(&BlueRotateCollision, (Gfx*)LOAD_ASSET_RAW(BlueRotateCollision.Model), 0.15f);*/ + GenerateCollisionMesh(&RedRotateCollision, (Gfx*)LOAD_ASSET_RAW(RedRotateCollision.Model), 1.0f); + GenerateCollisionMesh(&GreenRotateCollision, (Gfx*)LOAD_ASSET_RAW(GreenRotateCollision.Model), 1.0f); + GenerateCollisionMesh(&BlueRotateCollision, (Gfx*)LOAD_ASSET_RAW(BlueRotateCollision.Model), 1.0f); } void Gizmo::Tick() { @@ -244,10 +244,10 @@ void Gizmo::Scale() { void Gizmo::Draw() { if (Enabled) { DrawHandles(); - DebugCollision(&RedCollision, Pos, {0, 0, 0}, {0.05f, 0.05f, 0.05f}, RedCollision.Triangles); - DebugCollision(&BlueCollision, Pos, {90, 0, 0}, {0.05f, 0.05f, 0.05f}, BlueCollision.Triangles); - DebugCollision(&GreenCollision, Pos, {0, 90, 0}, {0.05f, 0.05f, 0.05f}, GreenCollision.Triangles); - DebugCollision(&RedRotateCollision, Pos, {0, 0, 0}, {0.15f, 0.15f, 0.15f}, RedRotateCollision.Triangles); + //DebugCollision(&RedCollision, Pos, {0, 0, 0}, {0.05f, 0.05f, 0.05f}, RedCollision.Triangles); + //DebugCollision(&BlueCollision, Pos, {90, 0, 0}, {0.05f, 0.05f, 0.05f}, BlueCollision.Triangles); + //DebugCollision(&GreenCollision, Pos, {0, 90, 0}, {0.05f, 0.05f, 0.05f}, GreenCollision.Triangles); + //DebugCollision(&RedRotateCollision, Pos, {0, 0, 0}, {0.15f, 0.15f, 0.15f}, RedRotateCollision.Triangles); //DebugCollision((uintptr_t)_selected, Pos, BlueRotateCollision.Triangles); //DebugCollision((uintptr_t)_selected, Pos, GreenRotateCollision.Triangles); } @@ -255,6 +255,9 @@ void Gizmo::Draw() { void Gizmo::DrawHandles() { Mat4 mainMtx; + Editor_MatrixIdentity((float(*)[4])&Mtx_RedX); + Editor_MatrixIdentity((float(*)[4])&Mtx_GreenY); + Editor_MatrixIdentity((float(*)[4])&Mtx_BlueZ); const char* blueHandle; const char* greenHandle; @@ -274,7 +277,7 @@ void Gizmo::DrawHandles() { _gizmoOffset = 8.0f; greenRot = {0, 90, 0}; blueRot = {90, 0, 0}; - scale = {1, 1, 1}; + scale = {0.3, 0.3, 0.3}; break; case TranslationMode::Rotate: center = nullptr; // No All_Axis drag button for Rotation @@ -317,19 +320,16 @@ void Gizmo::DrawHandles() { gSPDisplayList(gDisplayListHead++, (Gfx*)center); } - Mat4 RedXMtx; - ApplyMatrixTransformations(RedXMtx, FVector(Pos.x, Pos.y, Pos.z - _gizmoOffset), Rot, scale); - Editor_AddMatrix(RedXMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + ApplyMatrixTransformations((float(*)[4])&Mtx_RedX, FVector(Pos.x, Pos.y, Pos.z - _gizmoOffset), Rot, scale); + Editor_AddMatrix((float(*)[4])&Mtx_RedX, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, (Gfx*)redHandle); - Mat4 GreenYMtx; - ApplyMatrixTransformations(GreenYMtx, FVector(Pos.x - _gizmoOffset, Pos.y, Pos.z), greenRot, scale); - Editor_AddMatrix(GreenYMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + ApplyMatrixTransformations((float(*)[4])&Mtx_GreenY, FVector(Pos.x - _gizmoOffset, Pos.y, Pos.z), greenRot, scale); + Editor_AddMatrix((float(*)[4])&Mtx_GreenY, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, (Gfx*)greenHandle); - Mat4 BlueZMtx; - ApplyMatrixTransformations(BlueZMtx, FVector(Pos.x, Pos.y + _gizmoOffset, Pos.z), blueRot, scale); - Editor_AddMatrix(BlueZMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + ApplyMatrixTransformations((float(*)[4])&Mtx_BlueZ, FVector(Pos.x, Pos.y + _gizmoOffset, Pos.z), blueRot, scale); + Editor_AddMatrix((float(*)[4])&Mtx_BlueZ, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, (Gfx*)blueHandle); } } diff --git a/src/engine/editor/Gizmo.h b/src/engine/editor/Gizmo.h index efb53dbd3..ec5a864dc 100644 --- a/src/engine/editor/Gizmo.h +++ b/src/engine/editor/Gizmo.h @@ -60,6 +60,10 @@ public: GameObject GreenRotateCollision; GameObject BlueRotateCollision; + MtxF Mtx_RedX; + MtxF Mtx_GreenY; + MtxF Mtx_BlueZ; + FVector Pos; // Global scene view IRotator Rot = {0, 0, 0}; float AllAxisRadius = 3.0f; // Free move selection radius diff --git a/src/engine/editor/ObjectPicker.cpp b/src/engine/editor/ObjectPicker.cpp index 32061beef..cbbe477e3 100644 --- a/src/engine/editor/ObjectPicker.cpp +++ b/src/engine/editor/ObjectPicker.cpp @@ -68,10 +68,10 @@ void ObjectPicker::DragHandle() { // Is the gizmo being dragged? if (eGizmo.Enabled) { - float t; // No all_axis grab for rotate if (static_cast(CVarGetInteger("eGizmoMode", 0)) != Gizmo::TranslationMode::Rotate) { + float t; if (IntersectRaySphere(ray, eGizmo.Pos, eGizmo.AllAxisRadius, t)) { eGizmo.SelectedHandle = Gizmo::GizmoHandle::All_Axis; eGizmo._ray = ray.Direction; @@ -83,42 +83,35 @@ void ObjectPicker::DragHandle() { } for (auto tri = eGizmo.RedCollision.Triangles.begin(); tri < eGizmo.RedCollision.Triangles.end(); tri++) { - float t; - FVector pos = FVector(eGizmo.Pos.x, eGizmo.Pos.y, eGizmo.Pos.z - eGizmo._gizmoOffset); - - if (IntersectRayTriangle(ray, *tri, pos, t)) { + if (auto clickPos = QueryHandleIntersection(eGizmo.Mtx_RedX, ray, *tri)) { eGizmo.SelectedHandle = Gizmo::GizmoHandle::Z_Axis; eGizmo._ray = ray.Direction; - FVector clickPosition = ray.Origin + ray.Direction * t; - eGizmo._cursorOffset = eGizmo.Pos - clickPosition; - eGizmo.PickDistance = t; - return; // Stop checking objects if we selected a Gizmo handle + eGizmo._cursorOffset = eGizmo.Pos - *clickPos; + float distance = (*clickPos - ray.Origin).Magnitude(); + eGizmo.PickDistance = distance; + return; } } for (auto tri = eGizmo.GreenCollision.Triangles.begin(); tri < eGizmo.GreenCollision.Triangles.end(); tri++) { - float t; - FVector pos = (FVector(eGizmo.Pos.x - eGizmo._gizmoOffset, eGizmo.Pos.y, eGizmo.Pos.z)); - if (IntersectRayTriangle(ray, *tri, pos, t)) { + if (auto clickPos = QueryHandleIntersection(eGizmo.Mtx_GreenY, ray, *tri)) { eGizmo.SelectedHandle = Gizmo::GizmoHandle::X_Axis; eGizmo._ray = ray.Direction; - FVector clickPosition = ray.Origin + ray.Direction * t; - eGizmo._cursorOffset = eGizmo.Pos - clickPosition; - eGizmo.PickDistance = t; - return; // Stop checking objects if we selected a Gizmo handle + eGizmo._cursorOffset = eGizmo.Pos - *clickPos; + float distance = (*clickPos - ray.Origin).Magnitude(); + eGizmo.PickDistance = distance; + return; } } for (auto tri = eGizmo.BlueCollision.Triangles.begin(); tri < eGizmo.BlueCollision.Triangles.end(); tri++) { - float t; - FVector pos = FVector(eGizmo.Pos.x, eGizmo.Pos.y + eGizmo._gizmoOffset, eGizmo.Pos.z); - if (IntersectRayTriangle(ray, *tri, pos, t)) { + if (auto clickPos = QueryHandleIntersection(eGizmo.Mtx_BlueZ, ray, *tri)) { eGizmo.SelectedHandle = Gizmo::GizmoHandle::Y_Axis; eGizmo._ray = ray.Direction; - FVector clickPosition = ray.Origin + ray.Direction * t; - eGizmo._cursorOffset = eGizmo.Pos - clickPosition; - eGizmo.PickDistance = t; - return; // Stop checking objects if we selected a Gizmo handle + eGizmo._cursorOffset = eGizmo.Pos - *clickPos; + float distance = (*clickPos - ray.Origin).Magnitude(); + eGizmo.PickDistance = distance; + return; } } } @@ -128,6 +121,21 @@ void ObjectPicker::Draw() { if (_selected != NULL) { eGizmo.Draw(); } + + if (Debug) { + Mat4 CursorMtx; + IRotator rot = IRotator(0,0,0); + FVector scale = FVector(0.1, 0.1, 0.1); + FVector ray = ScreenRayTrace(); + + float x = (cameras[0].pos[0] + ray.x * 800); + float y = (cameras[0].pos[1] + ray.y * 800); + float z = (cameras[0].pos[2] + ray.z * 800); + + ApplyMatrixTransformations((float(*)[4])&CursorMtx, FVector(x, y, z), rot, scale); + Editor_AddMatrix((float(*)[4])&CursorMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gDisplayListHead++, (Gfx*)"__OTR__tracks/sphere"); + } } void ObjectPicker::FindObject(Ray ray, std::vector objects) { @@ -142,7 +150,8 @@ void ObjectPicker::FindObject(Ray ray, std::vector objects) { case GameObject::CollisionType::VTX_INTERSECT: for (const auto& tri : object->Triangles) { float t; - if (IntersectRayTriangle(ray, tri, *object->Pos, t)) { + //Ray localRay = RayToLocalSpace(eGizmo.Mtx_GreenY, ray); + if (IntersectRayTriangleAndTransform(ray, *object->Pos, tri, t)) { printf("\nSELECTED OBJECT\n\n"); _selected = object; found = true; @@ -165,7 +174,7 @@ void ObjectPicker::FindObject(Ray ray, std::vector objects) { // _selected = nullptr; // break; // } - printf("FOUND BOUNDING BOX OBJECT\n"); + printf("FOUND BOUNDING BOX OBJECT ray: %f %f %f obj %f %f %f\n", ray.Origin.x, ray.Origin.y, ray.Origin.z, object->Pos->x, object->Pos->y, object->Pos->z); found = true; //foundActor = &actor; //type = object->Type; diff --git a/src/engine/editor/ObjectPicker.h b/src/engine/editor/ObjectPicker.h index d1f64da89..e96bddcbc 100644 --- a/src/engine/editor/ObjectPicker.h +++ b/src/engine/editor/ObjectPicker.h @@ -19,10 +19,10 @@ namespace Editor { GameObject* _selected; private: bool _draw = false; - Vec3f _ray; GameObject* _lastSelected; s32 Inverse(MtxF* src, MtxF* dest); void Copy(MtxF* src, MtxF* dest); void Clear(MtxF* mf); + bool Debug = false; }; } diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index bd3fd8575..4ed5b4aa4 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -628,3 +628,11 @@ extern "C" uint32_t OTRGetGameRenderWidth() { extern "C" uint32_t OTRGetGameRenderHeight() { return gfx_current_dimensions.height; } + +extern "C" uint32_t OTRGetGameViewportWidth() { + return gfx_current_game_window_viewport.width; +} + +extern "C" uint32_t OTRGetGameViewportHeight() { + return gfx_current_game_window_viewport.height; +} \ No newline at end of file diff --git a/src/port/Engine.h b/src/port/Engine.h index 59bdb262f..fb277a5ad 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -72,6 +72,8 @@ class GameEngine { int16_t OTRGetRectDimensionFromRightEdge(float v); uint32_t OTRGetGameRenderWidth(); uint32_t OTRGetGameRenderHeight(); + uint32_t OTRGetGameViewportWidth(); + uint32_t OTRGetGameViewportHeight(); uint32_t OTRCalculateCenterOfAreaFromRightEdge(int32_t center); uint32_t OTRCalculateCenterOfAreaFromLeftEdge(int32_t center); private: @@ -101,6 +103,8 @@ int16_t OTRGetRectDimensionFromLeftEdge(float v); int16_t OTRGetRectDimensionFromRightEdge(float v); uint32_t OTRGetGameRenderWidth(void); uint32_t OTRGetGameRenderHeight(void); +uint32_t OTRGetGameViewportWidth(); +uint32_t OTRGetGameViewportHeight(); uint32_t OTRCalculateCenterOfAreaFromRightEdge(int32_t center); uint32_t OTRCalculateCenterOfAreaFromLeftEdge(int32_t center); #ifdef __cplusplus