// This function handles the collision between the ray and the ground // Information about the interaction is passed in colEntry void World::ground_collide_handler(const CollisionEntry& colEntry) { // Set the ball to the appropriate Z value for it to be exactly on the ground NodePath renderNp = m_windowFrameworkPtr->get_render(); float newZ = colEntry.get_surface_point(renderNp).get_z(); m_ballRootNp.set_z(newZ + 0.4); // Find the acceleration direction. First the surface normal is crossed with // the up vector to get a vector perpendicular to the slope LVector3f norm = colEntry.get_surface_normal(renderNp); LVector3f accelSide = norm.cross(UP); // Then that vector is crossed with the surface normal to get a vector that // points down the slope. By getting the acceleration in 3D like this rather // than in 2D, we reduce the amount of error per-frame, reducing jitter m_accelV = norm.cross(accelSide); }
// This function handles the collision between the ball and a wall void World::wall_collide_handler(const CollisionEntry& colEntry) { // First we calculate some numbers we need to do a reflection NodePath renderNp = m_windowFrameworkPtr->get_render(); // The normal of the wall LVector3f norm = colEntry.get_surface_normal(renderNp) * -1; // The current speed float curSpeed = m_ballV.length(); // The direction of travel LVector3f inVec = m_ballV / curSpeed; // Angle of incidence float velAngle = norm.dot(inVec); LPoint3f hitDir = colEntry.get_surface_point(renderNp) - m_ballRootNp.get_pos(); hitDir.normalize(); // The angle between the ball and the normal float hitAngle = norm.dot(hitDir); // Ignore the collision if the ball is either moving away from the wall // already (so that we don't accidentally send it back into the wall) // and ignore it if the collision isn't dead-on (to avoid getting caught on // corners) if(velAngle > 0 && hitAngle > .995) { // Standard reflection equation LVector3f reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec; // This makes the velocity half of what it was if the hit was dead-on // and nearly exactly what it was if this is a glancing blow m_ballV = reflectVec * (curSpeed * (((1-velAngle)*.5)+.5)); // Since we have a collision, the ball is already a little bit buried in // the wall. This calculates a vector needed to move it so that it is // exactly touching the wall LPoint3f disp = (colEntry.get_surface_point(renderNp) - colEntry.get_interior_point(renderNp)); LPoint3f newPos = m_ballRootNp.get_pos() + disp; m_ballRootNp.set_pos(newPos); } }
void Mouse::Run(void) { PStatCollector collector("Level:Mouse:Run"); collector.start(); //if (pointer.get_in_window()) { LPoint2f cursorPos = GetPositionRatio(); if (cursorPos != _lastMousePos) { _lastMousePos = cursorPos; _pickerRay->set_from_lens(_window->get_camera(0), cursorPos.get_x(), cursorPos.get_y()); _collisionTraverser.traverse(_window->get_render()); _collisionHandlerQueue->sort_entries(); //_hovering.Reset(); _hovering.hasDynObject = false; _hovering.dynObject = NodePath(); for (int i = 0 ; i < _collisionHandlerQueue->get_num_entries() ; ++i) { CollisionEntry* entry = _collisionHandlerQueue->get_entry(i); NodePath into = entry->get_into_node_path(); if (into.is_hidden()) continue ; switch (into.get_collide_mask().get_word()) { case ColMask::DynObject: if (!(_hovering.hasDynObject)) _hovering.SetDynObject(into); break ; } } } } collector.stop(); }
// If the ball hits a hole trigger, then it should fall in the hole. // This is faked rather than dealing with the actual physics of it. void World::lose_game(const CollisionEntry& entry) { // The triggers are set up so that the center of the ball should move to the // collision point to be in the hole NodePath renderNp = m_windowFrameworkPtr->get_render(); LPoint3f toPos = entry.get_interior_point(renderNp); // Stop the maze task PT(GenericAsyncTask) rollTaskPtr = DCAST(GenericAsyncTask, AsyncTaskManager::get_global_ptr()->find_task("rollTask")); if(rollTaskPtr != NULL) { AsyncTaskManager::get_global_ptr()->remove(rollTaskPtr); } // Move the ball into the hole over a short sequence of time. Then wait a // second and call start to reset the game // Note: Sequence is a python only class. We have to manage using CMetaInterval for the animation // with a callback event when the animation is done to callback on World::start() to restart the game. PT(CLerpNodePathInterval) lerp1Ptr = new CLerpNodePathInterval("lerp1", 0.1, CLerpInterval::BT_no_blend, true, false, m_ballRootNp, NodePath()); PT(CLerpNodePathInterval) lerp2Ptr = new CLerpNodePathInterval("lerp2", 0.1, CLerpInterval::BT_no_blend, true, false, m_ballRootNp, NodePath()); PT(WaitInterval) waitPtr = new WaitInterval(1); PT(CMetaInterval) cMetaIntervalPtr = new CMetaInterval("sequence"); if(lerp1Ptr == NULL || lerp2Ptr == NULL || waitPtr == NULL || cMetaIntervalPtr == NULL) { nout << "ERROR: out of memory" << endl; return; } float endPosZ = m_ballRootNp.get_pos().get_z() - 0.9; LVecBase3f midEndPos(toPos.get_x(), toPos.get_y(), 0.5*(m_ballRootNp.get_pos().get_z()+endPosZ)); lerp1Ptr->set_end_pos(midEndPos); LVecBase3f endPos(toPos.get_x(), toPos.get_y(), endPosZ); lerp2Ptr->set_end_pos(endPos); cMetaIntervalPtr->add_c_interval(lerp1Ptr, 0, CMetaInterval::RS_previous_end); cMetaIntervalPtr->add_c_interval(lerp2Ptr, 0, CMetaInterval::RS_previous_end); cMetaIntervalPtr->add_c_interval(waitPtr , 0, CMetaInterval::RS_previous_end); cMetaIntervalPtr->set_done_event("restartGame"); cMetaIntervalPtr->start(); EventHandler::get_global_event_handler()->add_hook("restartGame", call_start, this); PT(GenericAsyncTask) intervalManagerTaskPtr = DCAST(GenericAsyncTask, AsyncTaskManager::get_global_ptr()->find_task("intervalManagerTask")); if(intervalManagerTaskPtr == NULL) { intervalManagerTaskPtr = new GenericAsyncTask("intervalManagerTask", step_interval_manager, NULL); if(intervalManagerTaskPtr != NULL) { AsyncTaskManager::get_global_ptr()->add(intervalManagerTaskPtr); } } }
void Mouse::ClosestWaypoint(World* world, short current_floor) { //if (_mouseWatcher->has_mouse()) { PStatCollector collector("Level:Mouse:FindWaypoint"); collector.start(); PT(CollisionRay) pickerRay; PT(CollisionNode) pickerNode; NodePath pickerPath; CollisionTraverser collisionTraverser; PT(CollisionHandlerQueue) collisionHandlerQueue = new CollisionHandlerQueue(); LPoint2f cursorPos = GetPositionRatio(); static bool updated = false; static LPoint2f last_update; if (ABS(cursorPos.get_x() - last_update.get_x()) > 0.05 || ABS(cursorPos.get_y() - last_update.get_y()) > 0.05) updated = false; if (!(updated == false)) return ; last_update = cursorPos; updated = true; pickerNode = new CollisionNode("mouseRay2"); pickerPath = _camera.attach_new_node(pickerNode); pickerRay = new CollisionRay(); pickerNode->set_from_collide_mask(CollideMask(ColMask::WpPlane)); pickerNode->set_into_collide_mask(0); pickerRay->set_from_lens(_window->get_camera(0), cursorPos.get_x(), cursorPos.get_y()); pickerNode->add_solid(pickerRay); collisionTraverser.add_collider(pickerPath, collisionHandlerQueue); collisionTraverser.traverse(_window->get_render()); //collisionTraverser.traverse(world->floors[current_floor]); collisionHandlerQueue->sort_entries(); if (_hovering.waypoint_ptr && _hovering.hasWaypoint) _hovering.waypoint_ptr->SetSelected(false); _hovering.hasWaypoint = false; _hovering.waypoint_ptr = 0; for (int i = 0 ; i < collisionHandlerQueue->get_num_entries() ; ++i) { CollisionTraverser model_traverser; PT(CollisionHandlerQueue) handler_queue = new CollisionHandlerQueue(); CollisionEntry* entry = collisionHandlerQueue->get_entry(i); NodePath np = entry->get_into_node_path(); MapObject* map_object = world->GetMapObjectFromNodePath(np); LPoint3 pos; static LPoint3 spheresize = NodePathSize(world->model_sphere); if (!map_object || map_object->nodePath.is_hidden()) continue ; if (map_object->collider.type == Collider::MODEL) { CollideMask initial_collide_mask = map_object->render.get_collide_mask(); map_object->render.set_collide_mask(initial_collide_mask | CollideMask(ColMask::WpPlane)); model_traverser.add_collider(pickerPath, handler_queue); model_traverser.traverse(map_object->render); map_object->render.set_collide_mask(initial_collide_mask); if (handler_queue->get_num_entries() == 0) continue ; entry = handler_queue->get_entry(0); } pos = entry->get_surface_point(world->window->get_render()) - spheresize; _hovering.waypoint_ptr = world->waypoint_graph.GetClosest(pos); if (_hovering.waypoint_ptr) { _hovering.SetWaypoint(_hovering.waypoint_ptr->nodePath); _hovering.waypoint_ptr->SetSelected(true); } break ; } // Detaching seems to be causing some memory issues. //pickerPath.detach_node(); collector.stop(); } }