1717#include " engine.h"
1818#include " mesh_component.h"
1919#include " scene_loading.h"
20+ #include < cmath>
2021
2122#include < algorithm>
2223#include < chrono>
@@ -389,6 +390,10 @@ void Engine::handleMouseInput(float x, float y, uint32_t buttons) {
389390 // Finger just went down
390391 cameraControl.mouseLeftPressed = true ;
391392 cameraControl.firstMouse = true ;
393+ cameraControl.touchTotalDistance = 0 .0f ;
394+ cameraControl.touchDownX = x;
395+ cameraControl.touchDownY = y;
396+ cameraControl.touchStartTime = 0.0 ; // We'll increment this in Update
392397 }
393398
394399 if (cameraControl.firstMouse ) {
@@ -399,8 +404,20 @@ void Engine::handleMouseInput(float x, float y, uint32_t buttons) {
399404
400405 // Accumulate movement deltas. These will be applied in UpdateCameraControls
401406 // AFTER ImGui has updated its capture state (post-NewFrame).
402- cameraControl.pendingXOffset += (x - cameraControl.lastMouseX );
403- cameraControl.pendingYOffset += (y - cameraControl.lastMouseY );
407+ float dx = (x - cameraControl.lastMouseX );
408+ float dy = (y - cameraControl.lastMouseY );
409+ cameraControl.pendingXOffset += dx;
410+ cameraControl.pendingYOffset += dy;
411+ cameraControl.touchTotalDistance += std::sqrt (dx*dx + dy*dy);
412+
413+ #if defined(PLATFORM_ANDROID)
414+ // On Android, we map SWIPE to MOVEMENT (Forward/Backward, Left/Right)
415+ // if the touch didn't start on UI.
416+ if (!cameraControl.isFirstFrameOfInteraction && !cameraControl.startedOnImGui ) {
417+ cameraControl.touchMoveX = dx;
418+ cameraControl.touchMoveY = dy;
419+ }
420+ #endif
404421
405422 cameraControl.lastMouseX = x;
406423 cameraControl.lastMouseY = y;
@@ -457,15 +474,15 @@ void Engine::handleKeyInput(uint32_t key, bool pressed) {
457474 default :
458475 break ;
459476 }
460-
461- if (imguiSystem) {
462- imguiSystem->HandleKeyboard (key, pressed);
463- }
464477#else
465478 // Android uses different input handling via touch events
466479 (void ) key;
467480 (void ) pressed;
468481#endif
482+
483+ if (imguiSystem) {
484+ imguiSystem->HandleKeyboard (key, pressed);
485+ }
469486}
470487
471488void Engine::Update (TimeDelta deltaTime) {
@@ -659,6 +676,110 @@ void Engine::UpdateCameraControls(TimeDelta deltaTime) {
659676 // Check if ImGui wants to capture mouse input (updated in NewFrame)
660677 bool imguiWantsMouse = imguiSystem && imguiSystem->WantCaptureMouse ();
661678
679+ #if defined(PLATFORM_ANDROID)
680+ // --- Android: Ambitious Controls ---
681+ // 1. Accelerometer -> Rotation (Tilting)
682+ float ax, ay, az;
683+ float androidPitchOffset = 0 .0f ;
684+ float androidYawOffset = 0 .0f ;
685+ if (platform->GetAccelerometerData (&ax, &ay, &az)) {
686+ // Correct for display rotation (Portrait vs Landscape vs Reversed)
687+ ax = -ax;
688+ ay = -ay;
689+ float rawX = ax;
690+ float rawY = ay;
691+ int rotation = platform->GetDisplayRotation ();
692+ switch (rotation) {
693+ case 1 : // ROTATION_90 (Landscape Left)
694+ ax = -rawY; ay = rawX; break ;
695+ case 2 : // ROTATION_180 (Portrait Upside Down)
696+ ax = -rawX; ay = -rawY; break ;
697+ case 3 : // ROTATION_270 (Landscape Right)
698+ ax = rawY; ay = -rawX; break ;
699+ default : // ROTATION_0 (Portrait)
700+ break ;
701+ }
702+
703+ // If not calibrated, take current values as neutral
704+ if (!cameraControl.tiltCalibrated ) {
705+ cameraControl.tiltCenterX = ax;
706+ cameraControl.tiltCenterY = ay;
707+ cameraControl.tiltCalibrated = true ;
708+ }
709+
710+ float dax = ax - cameraControl.tiltCenterX ;
711+ float day = ay - cameraControl.tiltCenterY ;
712+
713+ // Auto-recalibration: If the phone is held steady (small delta from current center),
714+ // we slowly drift the center point towards the current reading.
715+ // This allows the "rest" position to adapt to the user's hands.
716+ float distFromCenter = std::sqrt (dax*dax + day*day);
717+ float dt = deltaTime.count () * 0 .001f ;
718+
719+ if (distFromCenter < 1 .5f ) {
720+ // Steady detection: If we're close to the center for a while, snap it.
721+ cameraControl.tiltSteadyTime += dt;
722+ if (cameraControl.tiltSteadyTime > 0 .5f ) {
723+ // Drift the center towards current value to "establish a new deadzone"
724+ float driftRate = 2 .0f * dt;
725+ cameraControl.tiltCenterX += dax * driftRate;
726+ cameraControl.tiltCenterY += day * driftRate;
727+ }
728+ } else {
729+ cameraControl.tiltSteadyTime = 0 .0f ;
730+ }
731+
732+ // Deadzone and immediate stop: if within deadzone, motion is ZERO.
733+ // Increased deadzone to 0.8f for more stability.
734+ if (std::abs (dax) < 0 .8f ) dax = 0 .0f ;
735+ if (std::abs (day) < 0 .8f ) day = 0 .0f ;
736+
737+ // We multiply by deltaTime to ensure consistent rotation speed across different frame rates.
738+ const float tiltSensitivity = 20 .0f ; // Degrees per second at max tilt
739+ androidYawOffset = dax * tiltSensitivity * dt;
740+ androidPitchOffset = -day * tiltSensitivity * dt;
741+ }
742+
743+ // 2. Swipe -> Movement
744+ float androidMoveForward = 0 .0f ;
745+ float androidMoveRight = 0 .0f ;
746+ if (cameraControl.mouseLeftPressed && !cameraControl.startedOnImGui ) {
747+ const float moveSensitivity = 0 .15f ;
748+ androidMoveRight = cameraControl.touchMoveX * moveSensitivity;
749+ androidMoveForward = -cameraControl.touchMoveY * moveSensitivity;
750+ }
751+ // Clear touch movement frame delta
752+ cameraControl.touchMoveX = 0 .0f ;
753+ cameraControl.touchMoveY = 0 .0f ;
754+
755+ // 3. Tap and Hold -> Reset Camera & Recalibrate Tilt
756+ bool isHoldingToReset = false ;
757+ if (cameraControl.mouseLeftPressed && !cameraControl.startedOnImGui ) {
758+ cameraControl.touchStartTime += deltaTime.count () * 0 .001f ;
759+ // If held for more than 0.5s without moving more than 10 pixels
760+ if (cameraControl.touchStartTime > 0 .5f && cameraControl.touchTotalDistance < 10 .0f ) {
761+ cameraControl.yaw = 0 .0f ;
762+ cameraControl.pitch = 0 .0f ;
763+
764+ // Force the camera to be level (horizon-aligned) during reset.
765+ // We extract the yaw from the base orientation and discard pitch/roll.
766+ glm::vec3 euler = glm::eulerAngles (cameraControl.baseOrientation );
767+ cameraControl.baseOrientation = glm::angleAxis (euler.y , glm::vec3 (0 .0f , 1 .0f , 0 .0f ));
768+
769+ // Recalibrate: current physical orientation becomes the new "zero"
770+ // We use the rotation-corrected values already calculated in ax/ay above.
771+ cameraControl.tiltCenterX = ax;
772+ cameraControl.tiltCenterY = ay;
773+
774+ androidYawOffset = 0 .0f ;
775+ androidPitchOffset = 0 .0f ;
776+ isHoldingToReset = true ;
777+ }
778+ } else {
779+ cameraControl.touchStartTime = 0 .0f ;
780+ }
781+ #endif
782+
662783 // INTERACTION LOCKING LOGIC:
663784 // If a touch began, we wait until ImGui has processed the first DOWN event (in NewFrame)
664785 // before deciding whether this drag belongs to the GUI or the 3D Scene.
@@ -672,24 +793,34 @@ void Engine::UpdateCameraControls(TimeDelta deltaTime) {
672793
673794 // Only apply rotation if the interaction started on the scene background
674795 if (!cameraControl.startedOnImGui ) {
796+ #if !defined(PLATFORM_ANDROID)
675797 float xOffset = cameraControl.pendingXOffset * cameraControl.mouseSensitivity ;
676798 float yOffset = cameraControl.pendingYOffset * cameraControl.mouseSensitivity ;
677799
678800 cameraControl.yaw -= xOffset;
679801 cameraControl.pitch -= yOffset;
680-
681- // Constrain pitch to avoid gimbal lock
682- if (cameraControl.pitch > 89 .0f )
683- cameraControl.pitch = 89 .0f ;
684- if (cameraControl.pitch < -89 .0f )
685- cameraControl.pitch = -89 .0f ;
802+ #endif
686803 }
687804 } else {
688805 // Reset locking state when finger is lifted
689806 cameraControl.isFirstFrameOfInteraction = true ;
690807 cameraControl.startedOnImGui = false ;
691808 }
692809
810+ #if defined(PLATFORM_ANDROID)
811+ // Apply Android tilt and swiping
812+ if (!isHoldingToReset) {
813+ cameraControl.yaw += androidYawOffset;
814+ cameraControl.pitch += androidPitchOffset;
815+ }
816+ #endif
817+
818+ // Constrain pitch to avoid gimbal lock
819+ if (cameraControl.pitch > 89 .0f )
820+ cameraControl.pitch = 89 .0f ;
821+ if (cameraControl.pitch < -89 .0f )
822+ cameraControl.pitch = -89 .0f ;
823+
693824 // Clear accumulated offsets after processing
694825 cameraControl.pendingXOffset = 0 .0f ;
695826 cameraControl.pendingYOffset = 0 .0f ;
@@ -745,6 +876,20 @@ void Engine::UpdateCameraControls(TimeDelta deltaTime) {
745876 position -= up * velocity;
746877 }
747878
879+ #if defined(PLATFORM_ANDROID)
880+ // Apply Android swipe-to-walk displacement
881+ // We use the same front/right vectors but apply the swipe deltas.
882+ // Note: androidMoveForward/Right are already calculated in the Android block above.
883+ position += front * androidMoveForward * cameraControl.cameraSpeed * 0 .02f ;
884+ position += right * androidMoveRight * cameraControl.cameraSpeed * 0 .02f ;
885+ #endif
886+
887+ #if defined(PLATFORM_ANDROID)
888+ // Apply Android swipe-based movement
889+ position += front * androidMoveForward;
890+ position += right * androidMoveRight;
891+ #endif
892+
748893 // Update camera position
749894 cameraTransform->SetPosition (position);
750895 // Apply rotation to the camera transform based on GLTF base orientation plus mouse deltas
@@ -998,33 +1143,12 @@ bool Engine::InitializeAndroid(android_app* app, const std::string& appName, boo
9981143
9991144 // Set mouse callback
10001145 platform->SetMouseCallback ([this ](float x, float y, uint32_t buttons) {
1001- // Check if ImGui wants to capture mouse input first
1002- bool imguiWantsMouse = imguiSystem && imguiSystem->WantCaptureMouse ();
1003-
1004- if (!imguiWantsMouse) {
1005- // Handle mouse click for ball throwing (right mouse button)
1006- if (buttons & 2 ) {
1007- // Right mouse button (bit 1)
1008- if (!cameraControl.mouseRightPressed ) {
1009- cameraControl.mouseRightPressed = true ;
1010- // Throw a ball on mouse click
1011- ThrowBall (x, y);
1012- }
1013- } else {
1014- cameraControl.mouseRightPressed = false ;
1015- }
1016- }
1017-
1018- if (imguiSystem) {
1019- imguiSystem->HandleMouse (x, y, buttons);
1020- }
1146+ handleMouseInput (x, y, buttons);
10211147 });
10221148
10231149 // Set keyboard callback
10241150 platform->SetKeyboardCallback ([this ](uint32_t key, bool pressed) {
1025- if (imguiSystem) {
1026- imguiSystem->HandleKeyboard (key, pressed);
1027- }
1151+ handleKeyInput (key, pressed);
10281152 });
10291153
10301154 // Set char callback
0 commit comments