@@ -40,40 +40,59 @@ bool Engine::Initialize(const std::string& appName, int width, int height, bool
4040
4141 // Set mouse callback
4242 platform->SetMouseCallback ([this ](float x, float y, uint32_t buttons) {
43- // Handle camera rotation when left mouse button is pressed
44- if (buttons & 1 ) { // Left mouse button (bit 0)
45- if (!cameraControl.mouseLeftPressed ) {
46- cameraControl.mouseLeftPressed = true ;
47- cameraControl.firstMouse = true ;
43+ // Check if ImGui wants to capture mouse input first
44+ bool imguiWantsMouse = imguiSystem && imguiSystem->WantCaptureMouse ();
45+
46+ if (!imguiWantsMouse) {
47+ // Handle mouse click for poke functionality (right mouse button)
48+ if (buttons & 2 ) { // Right mouse button (bit 1)
49+ if (!cameraControl.mouseRightPressed ) {
50+ cameraControl.mouseRightPressed = true ;
51+ // Perform poke on mouse click
52+ HandleMousePoke (x, y);
53+ }
54+ } else {
55+ cameraControl.mouseRightPressed = false ;
4856 }
4957
50- if (cameraControl.firstMouse ) {
58+ // Handle camera rotation when left mouse button is pressed
59+ if (buttons & 1 ) { // Left mouse button (bit 0)
60+ if (!cameraControl.mouseLeftPressed ) {
61+ cameraControl.mouseLeftPressed = true ;
62+ cameraControl.firstMouse = true ;
63+ }
64+
65+ if (cameraControl.firstMouse ) {
66+ cameraControl.lastMouseX = x;
67+ cameraControl.lastMouseY = y;
68+ cameraControl.firstMouse = false ;
69+ }
70+
71+ float xOffset = x - cameraControl.lastMouseX ;
72+ float yOffset = cameraControl.lastMouseY - y; // Reversed since y-coordinates go from bottom to top
5173 cameraControl.lastMouseX = x;
5274 cameraControl.lastMouseY = y;
53- cameraControl.firstMouse = false ;
54- }
55-
56- float xOffset = x - cameraControl.lastMouseX ;
57- float yOffset = cameraControl.lastMouseY - y; // Reversed since y-coordinates go from bottom to top
58- cameraControl.lastMouseX = x;
59- cameraControl.lastMouseY = y;
6075
61- xOffset *= cameraControl.mouseSensitivity ;
62- yOffset *= cameraControl.mouseSensitivity ;
76+ xOffset *= cameraControl.mouseSensitivity ;
77+ yOffset *= cameraControl.mouseSensitivity ;
6378
64- cameraControl.yaw += xOffset;
65- cameraControl.pitch += yOffset;
79+ cameraControl.yaw += xOffset;
80+ cameraControl.pitch += yOffset;
6681
67- // Constrain pitch to avoid gimbal lock
68- if (cameraControl.pitch > 89 .0f ) cameraControl.pitch = 89 .0f ;
69- if (cameraControl.pitch < -89 .0f ) cameraControl.pitch = -89 .0f ;
70- } else {
71- cameraControl.mouseLeftPressed = false ;
82+ // Constrain pitch to avoid gimbal lock
83+ if (cameraControl.pitch > 89 .0f ) cameraControl.pitch = 89 .0f ;
84+ if (cameraControl.pitch < -89 .0f ) cameraControl.pitch = -89 .0f ;
85+ } else {
86+ cameraControl.mouseLeftPressed = false ;
87+ }
7288 }
7389
7490 if (imguiSystem) {
7591 imguiSystem->HandleMouse (x, y, buttons);
7692 }
93+
94+ // Always perform hover detection (even when ImGui is active)
95+ HandleMouseHover (x, y);
7796 });
7897
7998 // Set keyboard callback
@@ -104,6 +123,7 @@ bool Engine::Initialize(const std::string& appName, int width, int height, bool
104123 case GLFW_KEY_PAGE_DOWN:
105124 cameraControl.moveDown = pressed;
106125 break ;
126+ default : break ;
107127 }
108128
109129 if (imguiSystem) {
@@ -137,7 +157,7 @@ bool Engine::Initialize(const std::string& appName, int width, int height, bool
137157 return false ;
138158 }
139159
140- // Initialize physics system
160+ // Initialize a physics system
141161 physicsSystem->SetRenderer (renderer.get ());
142162 if (!physicsSystem->Initialize ()) {
143163 return false ;
@@ -148,7 +168,7 @@ bool Engine::Initialize(const std::string& appName, int width, int height, bool
148168 return false ;
149169 }
150170
151- // Connect ImGui system to audio system for UI controls
171+ // Connect ImGui system to an audio system for UI controls
152172 imguiSystem->SetAudioSystem (audioSystem.get ());
153173
154174 initialized = true ;
@@ -207,19 +227,16 @@ void Engine::Cleanup() {
207227
208228Entity* Engine::CreateEntity (const std::string& name) {
209229 // Check if an entity with this name already exists
210- if (entityMap.find (name) != entityMap. end ( )) {
230+ if (entityMap.contains (name)) {
211231 return nullptr ;
212232 }
213233
214234 // Create the entity
215235 auto entity = std::make_unique<Entity>(name);
216- Entity* entityPtr = entity.get ();
217-
218236 // Add to the map and vector
219- entityMap[name] = entityPtr;
220237 entities.push_back (std::move (entity));
221238
222- return entityPtr ;
239+ return entities. back (). get () ;
223240}
224241
225242Entity* Engine::GetEntity (const std::string& name) {
@@ -310,10 +327,7 @@ ImGuiSystem* Engine::GetImGuiSystem() const {
310327}
311328
312329void Engine::Update (float deltaTime) {
313- // Check for completed background loading and create entities if ready
314- CheckAndCreateLoadedEntities ();
315-
316- // Update physics system
330+ // Update a physics system
317331 physicsSystem->Update (deltaTime);
318332
319333 // Update audio system
@@ -380,7 +394,7 @@ float Engine::CalculateDeltaTime() {
380394 return delta / 1000 .0f ; // Convert to seconds
381395}
382396
383- void Engine::HandleResize (int width, int height) {
397+ void Engine::HandleResize (int width, int height) const {
384398 // Update the active camera's aspect ratio
385399 if (activeCamera) {
386400 activeCamera->SetAspectRatio (static_cast <float >(width) / static_cast <float >(height));
@@ -397,28 +411,28 @@ void Engine::HandleResize(int width, int height) {
397411 }
398412}
399413
400- void Engine::UpdateCameraControls (float deltaTime) {
414+ void Engine::UpdateCameraControls (float deltaTime) const {
401415 if (!activeCamera) return ;
402416
403- // Get camera transform component
404- TransformComponent * cameraTransform = activeCamera->GetOwner ()->GetComponent <TransformComponent>();
417+ // Get a camera transform component
418+ auto * cameraTransform = activeCamera->GetOwner ()->GetComponent <TransformComponent>();
405419 if (!cameraTransform) return ;
406420
407421 // Calculate movement speed
408422 float velocity = cameraControl.cameraSpeed * deltaTime;
409423
410424 // Calculate camera direction vectors based on yaw and pitch
411425 glm::vec3 front;
412- front.x = cos (glm::radians (cameraControl.yaw )) * cos (glm::radians (cameraControl.pitch ));
413- front.y = sin (glm::radians (cameraControl.pitch ));
414- front.z = sin (glm::radians (cameraControl.yaw )) * cos (glm::radians (cameraControl.pitch ));
426+ front.x = cosf (glm::radians (cameraControl.yaw )) * cosf (glm::radians (cameraControl.pitch ));
427+ front.y = sinf (glm::radians (cameraControl.pitch ));
428+ front.z = sinf (glm::radians (cameraControl.yaw )) * cosf (glm::radians (cameraControl.pitch ));
415429 front = glm::normalize (front);
416430
417431 glm::vec3 up = glm::vec3 (0 .0f , 1 .0f , 0 .0f );
418432 glm::vec3 right = glm::normalize (glm::cross (front, up));
419433 up = glm::normalize (glm::cross (right, front));
420434
421- // Get current camera position
435+ // Get the current camera position
422436 glm::vec3 position = cameraTransform->GetPosition ();
423437
424438 // Apply movement based on input
@@ -444,25 +458,139 @@ void Engine::UpdateCameraControls(float deltaTime) {
444458 // Update camera position
445459 cameraTransform->SetPosition (position);
446460
447- // Update camera target based on direction
461+ // Update camera target based on a direction
448462 glm::vec3 target = position + front;
449463 activeCamera->SetTarget (target);
450464}
451465
452- void Engine::CheckAndCreateLoadedEntities () {
453- // Check if background loading is complete
454- if (g_loadingState.loadingComplete && !g_loadingState.loadedMaterials .empty ()) {
455- // Create entities from loaded materials on the main thread
456- CreateEntitiesFromLoadedMaterials (this );
466+ void Engine::HandleMousePoke (float mouseX, float mouseY) const {
467+ if (!activeCamera || !physicsSystem) {
468+ return ;
469+ }
470+
471+ // Get window dimensions
472+ int windowWidth, windowHeight;
473+ platform->GetWindowSize (&windowWidth, &windowHeight);
474+
475+ // Convert mouse coordinates to normalized device coordinates (-1 to 1)
476+ float ndcX = (2 .0f * mouseX) / static_cast <float >(windowWidth) - 1 .0f ;
477+ float ndcY = 1 .0f - (2 .0f * mouseY) / static_cast <float >(windowHeight);
478+
479+ // Get camera matrices
480+ glm::mat4 viewMatrix = activeCamera->GetViewMatrix ();
481+ glm::mat4 projMatrix = activeCamera->GetProjectionMatrix ();
482+
483+ // Calculate inverse matrices
484+ glm::mat4 invView = glm::inverse (viewMatrix);
485+ glm::mat4 invProj = glm::inverse (projMatrix);
486+
487+ // Convert NDC to world space
488+ glm::vec4 rayClip = glm::vec4 (ndcX, ndcY, -1 .0f , 1 .0f );
489+ glm::vec4 rayEye = invProj * rayClip;
490+ rayEye = glm::vec4 (rayEye.x , rayEye.y , -1 .0f , 0 .0f );
491+ glm::vec4 rayWorld = invView * rayEye;
457492
458- // Reset the loading complete flag
459- g_loadingState.loadingComplete = false ;
493+ // Get ray origin and direction
494+ glm::vec3 rayOrigin = activeCamera->GetPosition ();
495+ glm::vec3 rayDirection = glm::normalize (glm::vec3 (rayWorld));
496+
497+ // Perform raycast
498+ glm::vec3 hitPosition;
499+ glm::vec3 hitNormal;
500+ Entity* hitEntity = nullptr ;
501+
502+ if (physicsSystem->Raycast (rayOrigin, rayDirection, 1000 .0f , &hitPosition, &hitNormal, &hitEntity)) {
503+ if (hitEntity) {
504+ std::cout << " Mouse poke hit entity: " << hitEntity->GetName () << std::endl;
505+
506+ // Find or create rigid body for the entity
507+ RigidBody* rigidBody = nullptr ;
508+
509+ // Check if entity already has a rigid body (this is a simplified approach)
510+ // In a real implementation, you'd have a component system to track this
511+ rigidBody = physicsSystem->CreateRigidBody (hitEntity, CollisionShape::Box, 1 .0f );
512+
513+ if (rigidBody) {
514+ // Apply a small impulse in the direction of the ray
515+ glm::vec3 impulse = rayDirection * 0 .5f ; // Small force magnitude as requested
516+ rigidBody->ApplyImpulse (impulse, glm::vec3 (0 .0f ));
517+
518+ std::cout << " Applied poke impulse to " << hitEntity->GetName () << std::endl;
519+ }
520+ }
521+ } else {
522+ std::cout << " Mouse poke missed - no entity hit" << std::endl;
523+ }
524+ }
525+
526+ void Engine::HandleMouseHover (float mouseX, float mouseY) {
527+ if (!activeCamera || !physicsSystem) {
528+ return ;
460529 }
461530
462- // Check for loading errors
463- if (g_loadingState.loadingFailed ) {
464- std::cerr << " Background loading failed: " << g_loadingState.errorMessage << std::endl;
465- g_loadingState.loadingFailed = false ; // Reset the flag
531+ // Update current mouse position
532+ currentMouseX = mouseX;
533+ currentMouseY = mouseY;
534+
535+ // Get window dimensions
536+ int windowWidth, windowHeight;
537+ platform->GetWindowSize (&windowWidth, &windowHeight);
538+
539+ // Convert mouse coordinates to normalized device coordinates (-1 to 1)
540+ float ndcX = (2 .0f * mouseX) / static_cast <float >(windowWidth) - 1 .0f ;
541+ float ndcY = 1 .0f - (2 .0f * mouseY) / static_cast <float >(windowHeight);
542+
543+ // Get camera matrices
544+ glm::mat4 viewMatrix = activeCamera->GetViewMatrix ();
545+ glm::mat4 projMatrix = activeCamera->GetProjectionMatrix ();
546+
547+ // Calculate inverse matrices
548+ glm::mat4 invView = glm::inverse (viewMatrix);
549+ glm::mat4 invProj = glm::inverse (projMatrix);
550+
551+ // Convert NDC to world space
552+ glm::vec4 rayClip = glm::vec4 (ndcX, ndcY, -1 .0f , 1 .0f );
553+ glm::vec4 rayEye = invProj * rayClip;
554+ rayEye = glm::vec4 (rayEye.x , rayEye.y , -1 .0f , 0 .0f );
555+ glm::vec4 rayWorld = invView * rayEye;
556+
557+ // Get ray origin and direction
558+ glm::vec3 rayOrigin = activeCamera->GetPosition ();
559+ glm::vec3 rayDirection = glm::normalize (glm::vec3 (rayWorld));
560+
561+ // Perform raycast
562+ glm::vec3 hitPosition;
563+ glm::vec3 hitNormal;
564+ Entity* hitEntity = nullptr ;
565+
566+ if (physicsSystem->Raycast (rayOrigin, rayDirection, 1000 .0f , &hitPosition, &hitNormal, &hitEntity)) {
567+ if (hitEntity) {
568+ // Check if this entity is pokeable (has "_SMALL_POKEABLE" suffix)
569+ std::string entityName = hitEntity->GetName ();
570+
571+ if (entityName.find (" _SMALL_POKEABLE" ) != std::string::npos) {
572+ // Update a hovered entity if it's different from the current one
573+ if (hoveredEntity != hitEntity) {
574+ hoveredEntity = hitEntity;
575+ renderer->SetHighlightedEntity (hoveredEntity);
576+ std::cout << " Now hovering over pokeable entity: " << entityName << std::endl;
577+ }
578+ } else {
579+ // Clear hover if we're over a non-pokeable entity
580+ if (hoveredEntity != nullptr ) {
581+ std::cout << " No longer hovering over pokeable entity" << std::endl;
582+ hoveredEntity = nullptr ;
583+ renderer->SetHighlightedEntity (nullptr );
584+ }
585+ }
586+ }
587+ } else {
588+ // Clear hover if no entity is hit
589+ if (hoveredEntity != nullptr ) {
590+ std::cout << " No longer hovering over pokeable entity" << std::endl;
591+ hoveredEntity = nullptr ;
592+ renderer->SetHighlightedEntity (nullptr );
593+ }
466594 }
467595}
468596
0 commit comments