void VehicleObject::tickPhysics(float dt) { RW_UNUSED(dt); if( physVehicle ) { // todo: a real engine function float velFac = info->handling.maxVelocity; float engineForce = info->handling.acceleration * throttle * velFac; if (fabs(engineForce) >= 0.001f) { collision->getBulletBody()->activate(true); } float brakeF = getBraking(); if( handbrake ) { brakeF = 5.f; } for(int w = 0; w < physVehicle->getNumWheels(); ++w) { btWheelInfo& wi = physVehicle->getWheelInfo(w); if( info->handling.driveType == VehicleHandlingInfo::All || (info->handling.driveType == VehicleHandlingInfo::Forward && wi.m_bIsFrontWheel) || (info->handling.driveType == VehicleHandlingInfo::Rear && !wi.m_bIsFrontWheel)) { physVehicle->applyEngineForce(engineForce, w); } float brakeReal = 5.f * info->handling.brakeDeceleration * (wi.m_bIsFrontWheel? info->handling.brakeBias : 1.f - info->handling.brakeBias); physVehicle->setBrake(brakeReal * brakeF, w); if(wi.m_bIsFrontWheel) { float sign = std::signbit(steerAngle) ? -1.f : 1.f; physVehicle->setSteeringValue(std::min(info->handling.steeringLock*(3.141f/180.f), std::abs(steerAngle)) * sign, w); //physVehicle->setSteeringValue(std::min(3.141f/2.f, std::abs(steerAngle)) * sign, w); } } // Update passenger positions for (auto& seat : seatOccupants) { auto character = static_cast<CharacterObject*>(seat.second); glm::vec3 passPosition; if (character->isEnteringOrExitingVehicle()) { passPosition = getSeatEntryPositionWorld(seat.first); } else { passPosition = getPosition(); if (seat.first < info->seats.size()) { passPosition += getRotation() * (info->seats[seat.first].offset); } } seat.second->updateTransform(passPosition, getRotation()); } if( vehicle->type == VehicleData::BOAT ) { if( isInWater() ) { float sign = std::signbit(steerAngle) ? -1.f : 1.f; float steer = std::min(info->handling.steeringLock*(3.141f/180.f), std::abs(steerAngle)) * sign; auto orient = collision->getBulletBody()->getOrientation(); // Find the local-space velocity auto velocity = collision->getBulletBody()->getLinearVelocity(); velocity = velocity.rotate(-orient.getAxis(), orient.getAngle()); // Rudder force is proportional to velocity. float rAngle = steer * (velFac * 0.5f + 0.5f); btVector3 rForce = btVector3(1000.f * velocity.y() * rAngle, 0.f, 0.f) .rotate(orient.getAxis(), orient.getAngle()); btVector3 rudderPoint = btVector3(0.f, -info->handling.dimensions.y/2.f, 0.f) .rotate(orient.getAxis(), orient.getAngle()); collision->getBulletBody()->applyForce( rForce, rudderPoint); btVector3 rudderVector = btVector3(0.f, 1.f, 0.f) .rotate(orient.getAxis(), orient.getAngle()); collision->getBulletBody()->applyForce( rudderVector * engineForce * 100.f, rudderPoint); btVector3 dampforce( 10000.f * velocity.x(), velocity.y() * 100.f, 0.f ); collision->getBulletBody()->applyCentralForce(-dampforce.rotate(orient.getAxis(), orient.getAngle())); } } const auto& ws = getPosition(); auto wX = (int) ((ws.x + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE)); auto wY = (int) ((ws.y + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE)); btVector3 bbmin, bbmax; // This is in world space. collision->getBulletBody()->getAabb(bbmin, bbmax); float vH = bbmin.z(); float wH = 0.f; if( wX >= 0 && wX < WATER_HQ_DATA_SIZE && wY >= 0 && wY < WATER_HQ_DATA_SIZE ) { int i = (wX*WATER_HQ_DATA_SIZE) + wY; int hI = engine->data->realWater[i]; if( hI < NO_WATER_INDEX ) { wH = engine->data->waterHeights[hI]; wH += engine->data->getWaveHeightAt(ws); // If the vehicle is currently underwater if( vH <= wH ) { // and was not underwater here in the last tick if( _lastHeight >= wH ) { // we are for real, underwater inWater = true; } else if( inWater == false ) { // It's just a tunnel or something, we good. inWater = false; } } else { // The water is beneath us inWater = false; } } else { inWater = false; } } if( inWater ) { // Ensure that vehicles don't fall asleep at the top of a wave. if(! collision->getBulletBody()->isActive() ) { collision->getBulletBody()->activate(true); } float bbZ = info->handling.dimensions.z/2.f; float oZ = 0.f; oZ = -bbZ/2.f + (bbZ * (info->handling.percentSubmerged/120.f)); if( vehicle->type != VehicleData::BOAT ) { // Damper motion collision->getBulletBody()->setDamping(0.95f, 0.9f); } if( vehicle->type == VehicleData::BOAT ) { oZ = 0.f; } // Boats, Buoyancy offset is affected by the orientation of the chassis. // Vehicles, it isn't. glm::vec3 vFwd = glm::vec3(0.f, info->handling.dimensions.y/2.f, oZ), vBack = glm::vec3(0.f, -info->handling.dimensions.y/2.f, oZ); glm::vec3 vRt = glm::vec3( info->handling.dimensions.x/2.f, 0.f, oZ), vLeft = glm::vec3(-info->handling.dimensions.x/2.f, 0.f, oZ); vFwd = getRotation() * vFwd; vBack = getRotation() * vBack; vRt = getRotation() * vRt; vLeft = getRotation() * vLeft; // This function will try to keep v* at the water level. applyWaterFloat( vFwd); applyWaterFloat( vBack); applyWaterFloat( vRt); applyWaterFloat( vLeft); } else { if( vehicle->type == VehicleData::BOAT ) { collision->getBulletBody()->setDamping(0.1f, 0.8f); } else { collision->getBulletBody()->setDamping(0.05f, 0.0f); } } _lastHeight = vH; // Update hinge object rotations for(auto& it : dynamicParts) { if(it.second.body == nullptr) continue; if( it.second.moveToAngle ) { auto angledelta = it.second.targetAngle - it.second.constraint->getHingeAngle(); if( glm::abs(angledelta) <= 0.01f ) { it.second.constraint->enableAngularMotor(false, 1.f, 1.f); dynamicParts[it.first].moveToAngle = false; } else { it.second.constraint->enableAngularMotor(true, glm::sign(angledelta) * 5.f, 1.f); } } // If the part is moving quite fast and near the limit, lock it. /// @TODO not all parts rotate in the z axis. float zspeed = it.second.body->getAngularVelocity().getZ(); if(it.second.openAngle < 0.f) zspeed = -zspeed; if(zspeed >= PART_CLOSE_VELOCITY) { auto d = it.second.constraint->getHingeAngle() - it.second.closedAngle; if( glm::abs(d) < 0.05f ) { dynamicParts[it.first].moveToAngle = false; setPartLocked(&(it.second), true); } } } } }
bool Activities::ExitVehicle::update(CharacterObject *character, CharacterController *controller) { RW_UNUSED(controller); if (jacked) { const auto jacked_lhs = AnimCycle::CarJackedLHS; const auto jacked_rhs = AnimCycle::CarJackedRHS; const auto cycle_current = character->getCurrentCycle(); if (cycle_current == jacked_lhs || cycle_current == jacked_rhs) { if (character->animator->isCompleted(AnimIndexAction)) { return true; } } else { if (character->getCurrentVehicle() == nullptr) return true; auto vehicle = character->getCurrentVehicle(); auto seat = character->getCurrentSeat(); auto door = vehicle->getSeatEntryDoor(seat); RW_UNUSED(door); auto exitPos = vehicle->getSeatEntryPositionWorld(seat); auto exitPosLocal = vehicle->getSeatEntryPosition(seat); character->rotation = vehicle->getRotation(); // Exit the vehicle immediatley character->enterVehicle(nullptr, seat); character->setPosition(exitPos); if (exitPosLocal.x > 0.f) { character->playCycle(jacked_rhs); } else { character->playCycle(jacked_lhs); } // No need to open the door, it should already be open. } return false; } if (character->getCurrentVehicle() == nullptr) return true; auto vehicle = character->getCurrentVehicle(); auto seat = character->getCurrentSeat(); auto door = vehicle->getSeatEntryDoor(seat); auto exitPos = vehicle->getSeatEntryPositionWorld(seat); auto exitPosLocal = vehicle->getSeatEntryPosition(seat); auto cycle_exit = AnimCycle::CarGetOutLHS; if (exitPosLocal.x > 0.f) { cycle_exit = AnimCycle::CarGetOutRHS; } if (vehicle->getVehicle()->vehicletype_ == VehicleModelInfo::BOAT) { auto ppos = character->getPosition(); character->enterVehicle(nullptr, seat); character->setPosition(ppos); return true; } bool isDriver = vehicle->isOccupantDriver(character->getCurrentSeat()); // If the vehicle is going too fast, slow down if (isDriver) { if (!vehicle->canOccupantExit()) { vehicle->setBraking(1.f); return false; } } if (character->getCurrentCycle() == cycle_exit) { if (character->animator->isCompleted(AnimIndexAction)) { character->enterVehicle(nullptr, seat); character->setPosition(exitPos); if (isDriver) { // Apply the handbrake vehicle->setHandbraking(true); vehicle->setThrottle(0.f); } return true; } } else { character->playCycle(cycle_exit); if (door) { vehicle->setPartTarget(door, true, door->openAngle); } } return false; }