Editor Accurate mouse click drag objects

This commit is contained in:
MegaMech
2025-04-24 11:39:35 -06:00
parent e405e0c7bc
commit 2d25841852
8 changed files with 151 additions and 80 deletions
+70 -31
View File
@@ -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<FVector> 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) {
+9 -2
View File
@@ -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<FVector> QueryHandleIntersection(MtxF mtx, Ray ray, Triangle& tri);
void Editor_MatrixIdentity(Mat4 mtx);
void Editor_AddMatrix(Mat4 mtx, int32_t flags);
+21 -21
View File
@@ -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);
}
}
+4
View File
@@ -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
+34 -25
View File
@@ -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<Gizmo::TranslationMode>(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<GameObject*> objects) {
@@ -142,7 +150,8 @@ void ObjectPicker::FindObject(Ray ray, std::vector<GameObject*> 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<GameObject*> 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;
+1 -1
View File
@@ -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;
};
}