diff --git a/rwengine/src/ai/TrafficDirector.cpp b/rwengine/src/ai/TrafficDirector.cpp index da911703..4cee0a92 100644 --- a/rwengine/src/ai/TrafficDirector.cpp +++ b/rwengine/src/ai/TrafficDirector.cpp @@ -155,8 +155,6 @@ std::vector TrafficDirector::populateNearby( // We have not reached the limit of spawned pedestrians if (availablePeds > 0) { - static const glm::vec3 kSpawnOffset{0.f, 0.f, 1.f}; - int counter = availablePeds; // maxSpawn can be -1 for "as many as possible" if (maxSpawn > -1) { @@ -178,8 +176,8 @@ std::vector TrafficDirector::populateNearby( // Spawn a pedestrian from the available pool const uint16_t pedId = peds[std::uniform_int_distribution<>( 0, peds.size() - 1)(random)]; - auto ped = world->createPedestrian(pedId, - spawn->position + kSpawnOffset); + auto ped = world->createPedestrian(pedId, spawn->position); + ped->applyOffset(); ped->setLifetime(GameObject::TrafficLifetime); ped->controller->setGoal(CharacterController::TrafficWander); created.push_back(ped); @@ -190,8 +188,6 @@ std::vector TrafficDirector::populateNearby( // We have not reached the limit of spawned vehicles if (availableCars > 0) { - static const glm::vec3 kSpawnOffset{0.f, 0.f, 1.f}; - int counter = availableCars; // maxSpawn can be -1 for "as many as possible" if (maxSpawn > -1) { @@ -249,7 +245,8 @@ std::vector TrafficDirector::populateNearby( // Spawn a vehicle from the available pool const uint16_t carId = cars[std::uniform_int_distribution<>( 0, cars.size() - 1)(random)]; - auto vehicle = world->createVehicle(carId, next->position + kSpawnOffset + diff + laneOffset, orientation); + auto vehicle = world->createVehicle(carId, next->position + diff + laneOffset, orientation); + vehicle->applyOffset(); vehicle->setLifetime(GameObject::TrafficLifetime); vehicle->setHandbraking(false); diff --git a/rwengine/src/objects/CharacterObject.cpp b/rwengine/src/objects/CharacterObject.cpp index 41363fb4..851a2ea1 100644 --- a/rwengine/src/objects/CharacterObject.cpp +++ b/rwengine/src/objects/CharacterObject.cpp @@ -423,16 +423,23 @@ void CharacterObject::updateCharacter(float dt) { void CharacterObject::setPosition(const glm::vec3& pos) { auto realPos = pos; if (physCharacter) { - if (pos.z <= -99.f) { + if (pos.z <= -100.f) { realPos = engine->getGroundAtPosition(pos); } - btVector3 bpos(realPos.x, realPos.y, realPos.z + 1.0f); + btVector3 bpos(realPos.x, realPos.y, realPos.z); physCharacter->warp(bpos); } position = realPos; getClump()->getFrame()->setTranslation(pos); } +glm::vec3 CharacterObject::getCenterOffset() { + // Return an offset so that the feet are on the ground + const float z_offset = + physShape->getHalfHeight() + physShape->getRadius(); + return glm::vec3(0.f, 0.f, z_offset); +} + bool CharacterObject::isPlayer() const { return engine->state->playerObject == getGameObjectID(); } diff --git a/rwengine/src/objects/CharacterObject.hpp b/rwengine/src/objects/CharacterObject.hpp index cf0a8d9c..c76ef3eb 100644 --- a/rwengine/src/objects/CharacterObject.hpp +++ b/rwengine/src/objects/CharacterObject.hpp @@ -130,6 +130,8 @@ public: void setPosition(const glm::vec3& pos) override; + glm::vec3 getCenterOffset() override; + bool isPlayer() const; bool isDying() const; diff --git a/rwengine/src/objects/GameObject.hpp b/rwengine/src/objects/GameObject.hpp index aeb9da24..60c07482 100644 --- a/rwengine/src/objects/GameObject.hpp +++ b/rwengine/src/objects/GameObject.hpp @@ -148,6 +148,21 @@ public: */ void setHeading(float heading); + /** + * @brief getCenterOffset Returns the offset from center of mass to base of model + * This function should be overwritten by a derived class + */ + virtual glm::vec3 getCenterOffset() { + return glm::vec3(0.f, 0.f, 1.f); + } + + /** + * @brief applyOffset Applies the offset from getCenterOffset to the object + */ + void applyOffset() { + setPosition(getPosition() + getCenterOffset()); + } + struct DamageInfo { enum DamageType { Explosion, Burning, Bullet, Physics }; diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index 667105da..6f363acc 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -282,6 +282,17 @@ void VehicleObject::setPosition(const glm::vec3& pos) { } } +glm::vec3 VehicleObject::getCenterOffset() { + // Calculate the offset from the center to the base of the vehicle + btVector3 aabbMin; + btVector3 aabbMax; + + collision->getBulletBody()->getAabb(aabbMin, aabbMax); + float z_offset = (aabbMax.z() - aabbMin.z()) / 2; + + return glm::vec3(0.f, 0.f, z_offset); +} + void VehicleObject::setRotation(const glm::quat& orientation) { getClump()->getFrame()->setRotation(glm::mat3_cast(orientation)); if (collision->getBulletBody()) { diff --git a/rwengine/src/objects/VehicleObject.hpp b/rwengine/src/objects/VehicleObject.hpp index c8841b32..810406b6 100644 --- a/rwengine/src/objects/VehicleObject.hpp +++ b/rwengine/src/objects/VehicleObject.hpp @@ -103,6 +103,8 @@ public: void setRotation(const glm::quat& orientation) override; + glm::vec3 getCenterOffset() override; + void updateTransform(const glm::vec3& pos, const glm::quat& rot) override; VehicleModelInfo* getVehicle() const { diff --git a/rwengine/src/script/ScriptFunctions.hpp b/rwengine/src/script/ScriptFunctions.hpp index e3118e7c..09d78e18 100644 --- a/rwengine/src/script/ScriptFunctions.hpp +++ b/rwengine/src/script/ScriptFunctions.hpp @@ -21,7 +21,6 @@ * among many script modules and opcodes. */ namespace script { -const ScriptVec3 kSpawnOffset = ScriptVec3(0.f, 0.f, 1.f); inline void getObjectPosition(GameObject* object, ScriptFloat& x, ScriptFloat& y, ScriptFloat& z) { @@ -31,7 +30,8 @@ inline void getObjectPosition(GameObject* object, ScriptFloat& x, z = p.z; } inline void setObjectPosition(GameObject* object, const ScriptVec3& coord) { - object->setPosition(coord + script::kSpawnOffset); + object->setPosition(coord); + object->applyOffset(); } inline VehicleObject* getCharacterVehicle(CharacterObject* character) { diff --git a/rwengine/src/script/modules/GTA3ModuleImpl.inl b/rwengine/src/script/modules/GTA3ModuleImpl.inl index 8855628e..78136b2a 100644 --- a/rwengine/src/script/modules/GTA3ModuleImpl.inl +++ b/rwengine/src/script/modules/GTA3ModuleImpl.inl @@ -911,7 +911,8 @@ void opcode_0053(const ScriptArguments& args, const ScriptInt index, ScriptVec3 coord = script::getGround(args, coord); /// @todo fix the API interfaces that are now totally incoherent - auto character = args.getWorld()->createPlayer(coord + script::kSpawnOffset); + auto character = args.getWorld()->createPlayer(coord); + character->applyOffset(); player = static_cast(character->controller); args.getState()->playerObject = character->getGameObjectID(); } @@ -939,7 +940,8 @@ void opcode_0054(const ScriptArguments& args, const ScriptPlayer player, ScriptF */ void opcode_0055(const ScriptArguments& args, const ScriptPlayer player, ScriptVec3 coord) { RW_UNUSED(args); - player->getCharacter()->setPosition(coord + script::kSpawnOffset); + player->getCharacter()->setPosition(coord); + player->getCharacter()->applyOffset(); } /** @@ -1790,7 +1792,8 @@ void opcode_009a(const ScriptArguments& args, const ScriptPedType pedType, const RW_UNUSED(pedType); coord = script::getGround(args, coord); - character = args.getWorld()->createPedestrian(model, coord + script::kSpawnOffset); + character = args.getWorld()->createPedestrian(model, coord); + character->applyOffset(); character->setLifetime(GameObject::MissionLifetime); if (args.getThread()->isMission) { @@ -1939,7 +1942,8 @@ bool opcode_00a4(const ScriptArguments& args, const ScriptCharacter character, c void opcode_00a5(const ScriptArguments& args, const ScriptModelID model, ScriptVec3 coord, ScriptVehicle& vehicle) { // @todo calculate distance from centre of mass to base of model and apply it as spawnOffset coord = script::getGround(args, coord); - vehicle = args.getWorld()->createVehicle(model, coord + script::kSpawnOffset); + vehicle = args.getWorld()->createVehicle(model, coord); + vehicle->applyOffset(); vehicle->setLifetime(GameObject::MissionLifetime); if (args.getThread()->isMission) { @@ -9948,7 +9952,8 @@ void opcode_0376(const ScriptArguments& args, ScriptVec3 coord, const auto& pedGroup = data->pedgroups.at(groupId); const auto model = pedGroup.at(args.getVM()->getRandomNumber( static_cast(0), pedGroup.size() - 1)); - character = world->createPedestrian(model, coord + script::kSpawnOffset); + character = world->createPedestrian(model, coord); + character->applyOffset(); } /**