diff --git a/CMakeLists.txt b/CMakeLists.txt index f3ef797dd2..ca3fe0eeb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,10 @@ set(OENGINE_BULLET ${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.h + ${LIBDIR}/openengine/bullet/pmove.cpp + ${LIBDIR}/openengine/bullet/pmove.h + ${LIBDIR}/openengine/bullet/trace.cpp + ${LIBDIR}/openengine/bullet/trace.h ) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 539afe1c87..12a5956a98 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -148,7 +148,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mNewGame (false) , mUseSound (true) , mCompileAll (false) - , mFocusTDiff (0) , mScriptContext (0) , mFSStrict (false) , mCfgMgr(configurationManager) @@ -263,7 +262,6 @@ void OMW::Engine::setNewGame(bool newGame) void OMW::Engine::go() { - mFocusTDiff = 0; assert (!mCellName.empty()); assert (!mMaster.empty()); assert (!mOgre); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index c834ee438d..09d6bc8201 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -74,7 +74,6 @@ namespace OMW bool mNewGame; bool mUseSound; bool mCompileAll; - float mFocusTDiff; std::string mFocusName; std::map mFallbackMap; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ae0167639d..444fe9965f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -287,11 +287,12 @@ namespace MWClass { Ogre::Vector3 vector (0, 0, 0); - vector.x = - getMovementSettings (ptr).mLeftRight * 200; - vector.y = getMovementSettings (ptr).mForwardBackward * 200; + vector.x = - getMovementSettings (ptr).mLeftRight * 127; + vector.y = getMovementSettings (ptr).mForwardBackward * 127; + vector.z = getMovementSettings(ptr).mUpDown * 127; - if (getStance (ptr, Run, false)) - vector *= 2; + //if (getStance (ptr, Run, false)) + // vector *= 2; return vector; } diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index ac41244d1c..282fba3543 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -161,6 +161,8 @@ namespace MWDialogue bool DialogueManager::functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice) { + bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name()); + for (std::vector::const_iterator iter (info.selects.begin()); iter != info.selects.end(); ++iter) { @@ -195,6 +197,9 @@ namespace MWDialogue case 46://Same faction { + if (isCreature) + return false; + MWMechanics::NpcStats PCstats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); MWMechanics::NpcStats NPCstats = MWWorld::Class::get(actor).getNpcStats(actor); int sameFaction = 0; @@ -283,6 +288,8 @@ namespace MWDialogue bool DialogueManager::isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo::SelectStruct& select) const { + bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name()); + char type = select.selectRule[1]; if (type!='0') @@ -380,6 +387,9 @@ namespace MWDialogue return true; case '8':// not faction + if (isCreature) + return false; + if(select.type==ESM::VT_Int) { ESMS::LiveCellRef* npc = actor.get(); @@ -394,6 +404,9 @@ namespace MWDialogue return true; case '9':// not class + if (isCreature) + return false; + if(select.type==ESM::VT_Int) { ESMS::LiveCellRef* npc = actor.get(); @@ -408,6 +421,9 @@ namespace MWDialogue return true; case 'A'://not Race + if (isCreature) + return false; + if(select.type==ESM::VT_Int) { ESMS::LiveCellRef* npc = actor.get(); @@ -464,6 +480,8 @@ namespace MWDialogue bool DialogueManager::isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const { + bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name()); + // actor id if (!info.actor.empty()) if (toLower (info.actor)!=MWWorld::Class::get (actor).getId (actor)) @@ -472,6 +490,9 @@ namespace MWDialogue //NPC race if (!info.race.empty()) { + if (isCreature) + return false; + ESMS::LiveCellRef *cellRef = actor.get(); if (!cellRef) @@ -484,6 +505,9 @@ namespace MWDialogue //NPC class if (!info.clas.empty()) { + if (isCreature) + return false; + ESMS::LiveCellRef *cellRef = actor.get(); if (!cellRef) @@ -496,6 +520,9 @@ namespace MWDialogue //NPC faction if (!info.npcFaction.empty()) { + if (isCreature) + return false; + //MWWorld::Class npcClass = MWWorld::Class::get(actor); MWMechanics::NpcStats stats = MWWorld::Class::get(actor).getNpcStats(actor); std::map::iterator it = stats.mFactionRank.find(info.npcFaction); @@ -529,16 +556,18 @@ namespace MWDialogue } //check gender - ESMS::LiveCellRef* npc = actor.get(); - if(npc->base->flags&npc->base->Female) + if (!isCreature) { - if(static_cast (info.data.gender)==0) return false; + ESMS::LiveCellRef* npc = actor.get(); + if(npc->base->flags&npc->base->Female) + { + if(static_cast (info.data.gender)==0) return false; + } + else + { + if(static_cast (info.data.gender)==1) return false; + } } - else - { - if(static_cast (info.data.gender)==1) return false; - } - // check cell if (!info.cell.empty()) @@ -839,6 +868,9 @@ namespace MWDialogue std::string DialogueManager::getFaction() { + if (mActor.getTypeName() != typeid(ESM::NPC).name()) + return ""; + std::string factionID(""); MWMechanics::NpcStats stats = MWWorld::Class::get(mActor).getNpcStats(mActor); if(stats.mFactionRank.empty()) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 9a23758558..10c4cdcb4f 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -9,6 +9,7 @@ #include "mode.hpp" #include "../mwbase/environment.hpp" +#include "../mwsound/soundmanager.hpp" namespace { @@ -16,6 +17,7 @@ namespace { const char* mText; const char* mButtons[3]; + const char* mSound; ESM::Class::Specialization mSpecializations[3]; // The specialization for each answer }; @@ -25,6 +27,7 @@ namespace {"Draw your dagger, mercifully endings its life with a single thrust.", "Use herbs from your pack to put it to sleep.", "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."}, + "vo\\misc\\chargen qa1.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 2 @@ -32,6 +35,7 @@ namespace {"Work in the forge with him casting iron for a new plow.", "Gather herbs for your mother who is preparing dinner.", "Go catch fish at the stream using a net and line."}, + "vo\\misc\\chargen qa2.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 3 @@ -39,6 +43,7 @@ namespace {"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.", "Make up a story that makes your nickname a badge of honor instead of something humiliating.", "Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."}, + "vo\\misc\\chargen qa3.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 4 @@ -46,6 +51,7 @@ namespace {"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.", "Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.", "In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."}, + "vo\\misc\\chargen qa4.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 5 @@ -53,6 +59,7 @@ namespace {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", "Decide to put the extra money to good use and purchase items that would help your family?", "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"}, + "vo\\misc\\chargen qa5.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 6 @@ -60,6 +67,7 @@ namespace {"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.", "Leave the bag there, knowing that it is better not to get involved.", "Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."}, + "vo\\misc\\chargen qa6.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 7 @@ -67,6 +75,7 @@ namespace {"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.", "Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.", "Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."}, + "vo\\misc\\chargen qa7.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 8 @@ -74,6 +83,7 @@ namespace {"Position yourself between the pipe and your mother.", "Grab the hot pipe and try to push it away.", "Push your mother out of the way."}, + "vo\\misc\\chargen qa8.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 9 @@ -81,6 +91,7 @@ namespace {"Drop the sweetroll and step on it, then get ready for the fight.", "Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.", "Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."}, + "vo\\misc\\chargen qa9.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} }, // Question 10 @@ -88,6 +99,7 @@ namespace {"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.", "Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.", "Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."}, + "vo\\misc\\chargen qa10.wav", {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} } } }; @@ -479,6 +491,8 @@ void CharacterCreation::onCreateClassDialogBack() void CharacterCreation::onClassQuestionChosen(int _index) { + MWBase::Environment::get().getSoundManager()->stopSay(); + if (mGenerateClassQuestionDialog) mWM->removeDialog(mGenerateClassQuestionDialog); if (_index < 0 || _index >= 3) @@ -584,6 +598,8 @@ void CharacterCreation::showClassQuestionDialog() mGenerateClassQuestionDialog->setButtons(buttons); mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); mGenerateClassQuestionDialog->open(); + + MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps[mGenerateClassStep].mSound); } void CharacterCreation::onGenerateClassBack() diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index c2233f6265..7a56a0adf8 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -60,6 +60,7 @@ namespace MWInput A_CycleWeaponRight, A_ToggleSneak, //Toggles Sneak, add Push-Sneak later A_ToggleWalk, //Toggle Walking/Running + A_Crouch, A_QuickSave, A_QuickLoad, @@ -310,6 +311,9 @@ namespace MWInput poller.bind(A_MoveRight, KC_D); poller.bind(A_MoveForward, KC_W); poller.bind(A_MoveBackward, KC_S); + + poller.bind(A_Jump, KC_E); + poller.bind(A_Crouch, KC_LCONTROL); } //NOTE: Used to check for movement keys @@ -356,6 +360,13 @@ namespace MWInput } else player.setForwardBackward (0); + + if (poller.isDown(A_Jump)) + player.setUpDown (1); + else if (poller.isDown(A_Crouch)) + player.setUpDown (-1); + else + player.setUpDown (0); } // Switch between gui modes. Besides controlling the Gui windows diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index a555ac010a..11eb83151e 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -8,8 +8,9 @@ namespace MWMechanics { signed char mLeftRight; // 1: wants to move left, -1: wants to move right signed char mForwardBackward; // 1:wants to move forward, -1: wants to move backward + signed char mUpDown; - Movement() : mLeftRight (0), mForwardBackward (0) {} + Movement() : mLeftRight (0), mForwardBackward (0), mUpDown(0) {} }; } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 5922086a01..27c3f818e8 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -53,8 +53,10 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) Ogre::SceneNode* insert = cellnode->createChildSceneNode(); const float *f = ptr.getRefData().getPosition().pos; + insert->setPosition(f[0], f[1], f[2]); insert->setScale(ptr.getCellRef().scale, ptr.getCellRef().scale, ptr.getCellRef().scale); + // Convert MW rotation to a quaternion: f = ptr.getCellRef().pos.rot; diff --git a/apps/openmw/mwsound/mpgsnd_decoder.cpp b/apps/openmw/mwsound/mpgsnd_decoder.cpp index 9b91b4e74e..7f7a84889a 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.cpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.cpp @@ -189,7 +189,7 @@ void MpgSnd_Decoder::readAll(std::vector &output) { size_t pos = output.size(); output.resize(pos + mSndInfo.frames*mSndInfo.channels*2); - sf_readf_short(mSndFile, (short*)(output.data()+pos), mSndInfo.frames); + sf_readf_short(mSndFile, (short*)(&output[0]+pos), mSndInfo.frames); return; } // Fallback in case we don't know the total already diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 615def7019..ac4baa8b29 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -222,8 +222,8 @@ void OpenAL_SoundStream::play() for(ALuint i = 0;i < sNumBuffers;i++) { size_t got; - got = mDecoder->read(data.data(), data.size()); - alBufferData(mBuffers[i], mFormat, data.data(), got, mSampleRate); + got = mDecoder->read(&data[0], data.size()); + alBufferData(mBuffers[i], mFormat, &data[0], got, mSampleRate); } throwALerror(); @@ -299,11 +299,11 @@ bool OpenAL_SoundStream::process() if(finished) continue; - got = mDecoder->read(data.data(), data.size()); + got = mDecoder->read(&data[0], data.size()); finished = (got < data.size()); if(got > 0) { - alBufferData(bufid, mFormat, data.data(), got, mSampleRate); + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); alSourceQueueBuffers(mSource, 1, &bufid); } } while(processed > 0); @@ -595,7 +595,7 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname) alGenBuffers(1, &buf); throwALerror(); - alBufferData(buf, format, data.data(), data.size(), srate); + alBufferData(buf, format, &data[0], data.size(), srate); mBufferCache[fname] = buf; mBufferRefs[buf] = 1; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 4656efb6ed..9eefc7a28d 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -228,11 +228,47 @@ namespace MWSound } } + void SoundManager::say(const std::string& filename) + { + if(!mOutput->isInitialized()) + return; + try + { + float basevol = mMasterVolume * mSFXVolume; + std::string filePath = "Sound/"+filename; + + SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); + sound->mBaseVolume = basevol; + + mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); + } + catch(std::exception &e) + { + std::cout <<"Sound Error: "<second.first == ptr && snditer->second.second == "_say_sound") + { + snditer->first->stop(); + mActiveSounds.erase(snditer++); + } + else + snditer++; + } + } + + SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) { diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 8afd488e11..e1816cd1f3 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -97,11 +97,18 @@ namespace MWSound void say(MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. - /// \param filename name of a sound file in "Sound/Vo/" in the data directory. + /// \param filename name of a sound file in "Sound/" in the data directory. - bool sayDone(MWWorld::Ptr reference) const; + void say(const std::string& filename); + ///< Say some text, without an actor ref + /// \param filename name of a sound file in "Sound/" in the data directory. + + bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; ///< Is actor not speaking? + void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); + ///< Stop an actor speaking + SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound, independently of 3D-position diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index ed96f47d62..fb13e37c6a 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -21,23 +21,24 @@ namespace MWWorld PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : mRender(_rend), mEngine(0), mFreeFly (true) { - + + playerphysics = new playerMove; // Create physics. shapeLoader is deleted by the physic engine NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); - + playerphysics->mEngine = mEngine; } PhysicsSystem::~PhysicsSystem() { delete mEngine; - + } OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() { return mEngine; } - + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) { std::string handle = ""; @@ -65,6 +66,13 @@ namespace MWWorld return mEngine->rayTest2(from,to); } + void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight){ + playerphysics->hasWater = hasWater; + if(hasWater){ + playerphysics->waterHeight = waterHeight; + } + + } btVector3 PhysicsSystem::getRayPoint(float extent) { @@ -75,35 +83,35 @@ namespace MWWorld btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y); return result; } - + bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) { btVector3 _from, _to; _from = btVector3(from.x, from.y, from.z); _to = btVector3(to.x, to.y, to.z); - + std::pair result = mEngine->rayTest(_from, _to); - + return !(result.first == ""); } - - std::vector< std::pair > PhysicsSystem::doPhysics (float duration, - const std::vector >& actors) + void PhysicsSystem::doPhysics(float dt, const std::vector >& actors) { //set the DebugRenderingMode. To disable it,set it to 0 //eng->setDebugRenderingMode(1); - - + //set the walkdirection to 0 (no movement) for every actor) for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { OEngine::Physic::PhysicActor* act = it->second; act->setWalkDirection(btVector3(0,0,0)); } - - - + playerMove::playercmd& pm_ref = playerphysics->cmd; + + pm_ref.rightmove = 0; + pm_ref.forwardmove = 0; + pm_ref.upmove = 0; + //playerphysics->ps.move_type = PM_NOCLIP; for (std::vector >::const_iterator iter (actors.begin()); iter!=actors.end(); ++iter) @@ -118,21 +126,24 @@ namespace MWWorld Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); Ogre::Quaternion yawQuat = yawNode->getOrientation(); - Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); - Ogre::Quaternion both = yawQuat * pitchQuat; - - - //playerphysics->ps.viewangles.z = both.getPitch().valueDegrees(); - - + Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); + + // unused + //Ogre::Quaternion both = yawQuat * pitchQuat; + + playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); + playerphysics->ps.viewangles.z = 0; + playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; + if(mFreeFly) { - Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - - - - + + pm_ref.rightmove = -dir1.x; + pm_ref.forwardmove = dir1.z; + pm_ref.upmove = dir1.y; + + //std::cout << "Current angle" << yawQuat.getYaw().valueDegrees() - 90<< "\n"; //playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); //std::cout << "Pitch: " << yawQuat.getPitch() << "Yaw:" << yawQuat.getYaw() << "Roll: " << yawQuat.getRoll() << "\n"; @@ -140,31 +151,65 @@ namespace MWWorld } else { + Ogre::Quaternion quat = yawNode->getOrientation(); Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - - + + pm_ref.rightmove = -dir1.x; + pm_ref.forwardmove = dir1.z; + pm_ref.upmove = dir1.y; + + + dir = 0.025*(quat*dir1); } - + //set the walk direction act->setWalkDirection(btVector3(dir.x,-dir.z,dir.y)); } - mEngine->stepSimulation(duration); - + mEngine->stepSimulation(dt); + } + + std::vector< std::pair > PhysicsSystem::doPhysicsFixed ( + const std::vector >& actors) + { + Pmove(playerphysics); + std::vector< std::pair > response; for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { btVector3 newPos = it->second->getPosition(); - + Ogre::Vector3 coord(newPos.x(), newPos.y(), newPos.z()); - + if(it->first == "player"){ + + coord = playerphysics->ps.origin; + //std::cout << "ZCoord: " << coord.z << "\n"; + //std::cout << "Coord" << coord << "\n"; + //coord = Ogre::Vector3(coord.x, coord.z, coord.y); //x, z, -y + + } + + response.push_back(std::pair(it->first, coord)); } - + return response; } + + void PhysicsSystem::addHeightField (float* heights, + int x, int y, float yoffset, + float triSize, float sqrtVerts) + { + mEngine->addHeightField(heights, x, y, yoffset, triSize, sqrtVerts); + } + + void PhysicsSystem::removeHeightField (int x, int y) + { + mEngine->removeHeightField(x, y); + } + void PhysicsSystem::addObject (const std::string& handle, const std::string& mesh, const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) { @@ -207,7 +252,14 @@ namespace MWWorld { // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 - act->setPosition(btVector3(position.x,position.y,position.z)); + if (handle == "player") + { + playerphysics->ps.origin = position; + } + else + { + act->setPosition(btVector3(position.x,position.y,position.z)); + } } } @@ -228,6 +280,11 @@ namespace MWWorld bool PhysicsSystem::toggleCollisionMode() { + if(playerphysics->ps.move_type==PM_NOCLIP) + playerphysics->ps.move_type=PM_NORMAL; + + else + playerphysics->ps.move_type=PM_NOCLIP; for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { if (it->first=="player") @@ -258,7 +315,12 @@ namespace MWWorld } void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + + // unused + //Ogre::Vector3 objPos = node->getPosition(); + addObject (node->getName(), model, node->getOrientation(), node->getScale().x, node->getPosition()); } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index b88ff23b16..1af6bcca2f 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -5,6 +5,7 @@ #include #include #include "ptr.hpp" +#include namespace MWWorld { @@ -15,8 +16,11 @@ namespace MWWorld PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); - std::vector< std::pair > doPhysics (float duration, - const std::vector >& actors); + void doPhysics(float duration, const std::vector >& actors); + ///< do physics with dt - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed + + std::vector< std::pair > doPhysicsFixed (const std::vector >& actors); + ///< do physics with fixed timestep - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed void addObject (const std::string& handle, const std::string& mesh, const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position); @@ -24,6 +28,12 @@ namespace MWWorld void addActor (const std::string& handle, const std::string& mesh, const Ogre::Vector3& position); + void addHeightField (float* heights, + int x, int y, float yoffset, + float triSize, float sqrtVerts); + + void removeHeightField (int x, int y); + void removeObject (const std::string& handle); void moveObject (const std::string& handle, const Ogre::Vector3& position); @@ -49,11 +59,13 @@ namespace MWWorld OEngine::Physic::PhysicEngine* getEngine(); + void setCurrentWater(bool hasWater, int waterHeight); + private: OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; bool mFreeFly; - + playerMove* playerphysics; PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5ed9aeaffc..0b1483ff8f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -85,6 +85,14 @@ namespace MWWorld MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; } + void Player::setUpDown(int value) + { + MWWorld::Ptr ptr = getPlayer(); + + + + MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; + } void Player::toggleRunning() { @@ -100,4 +108,5 @@ namespace MWWorld MWWorld::Ptr ptr = getPlayer(); return MWWorld::Class::get(ptr).getNpcStats(ptr).mDrawState; } + } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 8dcd9fcc65..e199f17e98 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -116,6 +116,7 @@ namespace MWWorld void setLeftRight (int value); void setForwardBackward (int value); + void setUpDown(int value); void toggleRunning(); }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 5e4556fc70..6f9f3ed3ed 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -75,11 +75,14 @@ namespace MWWorld // silence annoying g++ warning - for (std::vector::const_iterator iter (functor.mHandles.begin()); - iter!=functor.mHandles.end(); ++iter){ - Ogre::SceneNode* node = *iter; + for (std::vector::const_iterator iter2 (functor.mHandles.begin()); + iter2!=functor.mHandles.end(); ++iter2){ + Ogre::SceneNode* node = *iter2; mPhysics->removeObject (node->getName()); } + + if (!((*iter)->cell->data.flags & ESM::Cell::Interior)) + mPhysics->removeHeightField( (*iter)->cell->data.gridX, (*iter)->cell->data.gridY ); } mRendering.removeCell(*iter); @@ -103,20 +106,36 @@ namespace MWWorld std::pair result = mActiveCells.insert(cell); - if(result.second){ - insertCell(*cell); - mRendering.cellAdded(cell); - mRendering.configureAmbient(*cell); - mRendering.requestMap(cell); - mRendering.configureAmbient(*cell); - } + if(result.second) + { + insertCell(*cell); + mRendering.cellAdded (cell); + + float verts = ESM::Land::LAND_SIZE; + float worldsize = ESM::Land::REAL_SIZE; + + if (!(cell->cell->data.flags & ESM::Cell::Interior)) + { + ESM::Land* land = mWorld->getStore().lands.search(cell->cell->data.gridX,cell->cell->data.gridY); + mPhysics->addHeightField (land->landData->heights, + cell->cell->data.gridX, cell->cell->data.gridY, + 0, ( worldsize/(verts-1) ), verts); + } + + mRendering.configureAmbient(*cell); + mRendering.requestMap(cell); + mRendering.configureAmbient(*cell); + + } } void Scene::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, bool adjustPlayerPos) { + bool hasWater = cell->cell->data.flags & cell->cell->HasWater; + mPhysics->setCurrentWater(hasWater, cell->cell->water); if (adjustPlayerPos) mWorld->getPlayer().setPos (position.pos[0], position.pos[1], position.pos[2]); diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 3de14f158f..4adaf79183 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -215,6 +215,7 @@ namespace MWWorld setFallbackValues(fallbackMap); + lastTick = mTimer.getMilliseconds(); } @@ -559,8 +560,9 @@ namespace MWWorld } } - void World::moveObjectImp (Ptr ptr, float x, float y, float z) + bool World::moveObjectImp (Ptr ptr, float x, float y, float z) { + bool ret = false; ptr.getRefData().getPosition().pos[0] = x; ptr.getRefData().getPosition().pos[1] = y; ptr.getRefData().getPosition().pos[2] = z; @@ -582,6 +584,7 @@ namespace MWWorld if (currentCell->cell->data.gridX!=cellX || currentCell->cell->data.gridY!=cellY) { mWorldScene->changeCell (cellX, cellY, mPlayer->getPlayer().getRefData().getPosition(), false); + ret = true; } } @@ -591,6 +594,8 @@ namespace MWWorld /// \todo cell change for non-player ref mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); + + return ret; } void World::moveObject (Ptr ptr, float x, float y, float z) @@ -632,29 +637,50 @@ namespace MWWorld void World::doPhysics (const std::vector >& actors, float duration) { - std::vector< std::pair > vectors = mPhysics->doPhysics (duration, actors); + mPhysics->doPhysics(duration, actors); - std::vector< std::pair >::iterator player = vectors.end(); + const int tick = 16; // 16 ms ^= 60 Hz - for (std::vector< std::pair >::iterator it = vectors.begin(); - it!= vectors.end(); ++it) + // Game clock part of the loop, contains everything that has to be executed in a fixed timestep + long long dt = mTimer.getMilliseconds() - lastTick; + if (dt >= 100) { - if (it->first=="player") + // throw away wall clock time if necessary to keep the framerate above the minimum of 10 fps + lastTick += (dt - 100); + dt = 100; + } + while (dt >= tick) + { + dt -= tick; + lastTick += tick; + + std::vector< std::pair > vectors = mPhysics->doPhysicsFixed (actors); + + std::vector< std::pair >::iterator player = vectors.end(); + + for (std::vector< std::pair >::iterator it = vectors.begin(); + it!= vectors.end(); ++it) { - player = it; + if (it->first=="player") + { + player = it; + } + else + { + MWWorld::Ptr ptr = getPtrViaHandle (it->first); + moveObjectImp (ptr, it->second.x, it->second.y, it->second.z); + } } - else + + // Make sure player is moved last (otherwise the cell might change in the middle of an update + // loop) + if (player!=vectors.end()) { - MWWorld::Ptr ptr = getPtrViaHandle (it->first); - moveObjectImp (ptr, it->second.x, it->second.y, it->second.z); + if (moveObjectImp (getPtrViaHandle (player->first), + player->second.x, player->second.y, player->second.z) == true) + return; // abort the current loop if the cell has changed } } - - // Make sure player is moved last (otherwise the cell might change in the middle of an update - // loop) - if (player!=vectors.end()) - moveObjectImp (getPtrViaHandle (player->first), - player->second.x, player->second.y, player->second.z); } bool World::toggleCollisionMode() @@ -789,7 +815,8 @@ namespace MWWorld std::vector < std::pair < float, std::string > >::iterator it = results.begin(); while (it != results.end()) { - if ( getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) + if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain + || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) { it = results.erase(it); } diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 77e5bcef62..7359f8b902 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -22,6 +22,8 @@ #include #include +#include + namespace Ogre { class Vector3; @@ -100,9 +102,13 @@ namespace MWWorld int mNumFacing; std::map mFallback; + unsigned long lastTick; + Ogre::Timer mTimer; + int getDaysPerMonth (int month) const; - void moveObjectImp (Ptr ptr, float x, float y, float z); + bool moveObjectImp (Ptr ptr, float x, float y, float z); + ///< @return true if the active cell (cell player is in) changed public: diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index e7da9f0850..42853d8cfa 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -1,6 +1,7 @@ #include "physic.hpp" #include #include +#include #include //#include #include "CMotionState.h" @@ -10,6 +11,8 @@ #include "BtOgreGP.h" #include "BtOgreExtras.h" +#include + #define BIT(x) (1<<(x)) namespace OEngine { @@ -161,10 +164,12 @@ namespace Physic // The actual physics solver solver = new btSequentialImpulseConstraintSolver; + //btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); pairCache = new btSortedOverlappingPairCache(); + //pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); - broadphase = new btDbvtBroadphase(pairCache); + broadphase = new btDbvtBroadphase(); // The world. dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); @@ -253,6 +258,60 @@ namespace Physic delete mShapeLoader; } + void PhysicEngine::addHeightField(float* heights, + int x, int y, float yoffset, + float triSize, float sqrtVerts) + { + const std::string name = "HeightField_" + + boost::lexical_cast(x) + "_" + + boost::lexical_cast(y); + + // find the minimum and maximum heights (needed for bullet) + float minh; + float maxh; + for (int i=0; imaxh) maxh = h; + if (hsetUseDiamondSubdivision(true); + + btVector3 scl(triSize, triSize, 1); + hfShape->setLocalScaling(scl); + + CMotionState* newMotionState = new CMotionState(this,name); + + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape); + RigidBody* body = new RigidBody(CI,name); + body->collide = true; + body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f)); + + addRigidBody(body); + } + + void PhysicEngine::removeHeightField(int x, int y) + { + const std::string name = "HeightField_" + + boost::lexical_cast(x) + "_" + + boost::lexical_cast(y); + + removeRigidBody(name); + deleteRigidBody(name); + } + RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { //get the shape from the .nif @@ -334,7 +393,7 @@ namespace Physic void PhysicEngine::stepSimulation(double deltaT) { - dynamicsWorld->stepSimulation(deltaT,1,1/50.); + dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 8d177efda8..ba241b2b77 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -140,6 +140,18 @@ namespace Physic */ RigidBody* createRigidBody(std::string mesh,std::string name,float scale); + /** + * Add a HeightField to the simulation + */ + void addHeightField(float* heights, + int x, int y, float yoffset, + float triSize, float sqrtVerts); + + /** + * Remove a HeightField from the simulation + */ + void removeHeightField(int x, int y); + /** * Add a RigidBody to the simulation */ diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp new file mode 100644 index 0000000000..591f1869f8 --- /dev/null +++ b/libs/openengine/bullet/pmove.cpp @@ -0,0 +1,2095 @@ +/* +This source file is a *modified* version of bg_pmove.c from the Quake 3 Arena source code, +which was released under the GNU GPL (v2) in 2005. +Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. +*/ + + +#include "pmove.h" + + + +//#include "bprintf.h" + +//#include "..\..\ESMParser\ESMParser\CELL.h" + +//#include "GameTime.h" + +//#include "Object.h" + +//#include "Sound.h" + +//#include "..\..\ESMParser\ESMParser\SNDG.h" +//#include "..\..\ESMParser\ESMParser\SOUN.h" + +#include + +//SceneInstance* global_lastscene = NULL; + +// Forward declaration: +void PM_AirMove(); + +static playerMove* pm = NULL; + +//extern std::map ExtCellLookup; + +static struct playermoveLocal +{ + playermoveLocal() : frametime(1.0f / 20.0f), groundPlane(true), walking(true), msec(50) + { + forward = Ogre::Vector3(0.0f, 0.0f, 0.0f); + right = Ogre::Vector3(0.0f, 0.0f, 0.0f); + up = Ogre::Vector3(0.0f, 0.0f, 0.0f); + + previous_origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); + previous_velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); + } + + traceResults groundTrace; + + //SceneInstance* scene; + + float frametime; // in seconds (usually something like 0.01f) + float impactSpeed; + + Ogre::Vector3 forward; + Ogre::Vector3 right; + Ogre::Vector3 up; + + int msec; + + Ogre::Vector3 previous_origin, previous_velocity; + + int previous_waterlevel; // the waterlevel before this pmove + + bool groundPlane; // if we're standing on a groundplane this frame + + bool walking; + int waterHeight; + bool hasWater; + bool isInterior; + //Object* traceObj; + +} pml; + +static inline void PM_ClipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, const float overbounce) +{ + float backoff; + //float change; + //int i; + + // backoff = in dot normal + //backoff = DotProduct (in, normal); + backoff = in.dotProduct(normal); + + if ( backoff < 0 ) + backoff *= overbounce; + else + backoff /= overbounce; + + // change = normal * backoff + // out = in - change + /*for ( i=0 ; i<3 ; i++ ) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + + }*/ + float changex = normal.x * backoff; + out.x = in.x - changex; + float changey = normal.y * backoff; + out.y = in.y - changey; + float changez = normal.z * backoff; + out.z = in.z - changez; +} + +float VectorNormalize2( const Ogre::Vector3& v, Ogre::Vector3& out) +{ + float length, ilength; + + length = v.x * v.x+ v.y * v.y + v.z * v.z; + length = sqrt(length); + + if (length) + { +#ifndef Q3_VM // bk0101022 - FPE related +// assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) ); +#endif + ilength = 1 / length; + out.x= v.x * ilength; + out.y = v.y * ilength; + out.z = v.z * ilength; + } else + { +#ifndef Q3_VM // bk0101022 - FPE related +// assert( ((Q_fabs(v[0])==0.0f) && (Q_fabs(v[1])==0.0f) && (Q_fabs(v[2])==0.0f)) ); +#endif + //VectorClear( out ); + out.x = 0; out.y = 0; out.z = 0; + } + + return length; + +} + + +float VectorNormalize(Ogre::Vector3& out) +{ + float length, ilength; + + length = out.x * out.x + out.y * out.y + out.z * out.z; + length = sqrt(length); + + if (length) + { +#ifndef Q3_VM // bk0101022 - FPE related +// assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) ); +#endif + ilength = 1 / length; + out.x = out.x * ilength; + out.y = out.y * ilength; + out.z = out.z * ilength; + } + + return length; + +} + +/* +================== +PM_SlideMove + +Returns qtrue if the velocity was clipped in some way +================== +*/ + +bool PM_SlideMove( bool gravity ) +{ + int bumpcount, numbumps; + Ogre::Vector3 dir; + float d; + int numplanes; + Ogre::Vector3 planes[MAX_CLIP_PLANES]; + Ogre::Vector3 primal_velocity; + Ogre::Vector3 clipVelocity; + int i, j, k; + struct traceResults trace; + Ogre::Vector3 end; + float time_left; + float into; + Ogre::Vector3 endVelocity; + Ogre::Vector3 endClipVelocity; + + numbumps = 4; + + // primal_velocity = pm->ps->velocity + //VectorCopy (pm->ps->velocity, primal_velocity); + primal_velocity = pm->ps.velocity; + + if ( gravity ) + { + // endVelocity = pm->ps->velocity - vec3(0, 0, pm->ps->gravity * pml.frametime) + //VectorCopy( pm->ps->velocity, endVelocity ); + endVelocity = pm->ps.velocity; + //endVelocity[2] -= pm->ps->gravity * pml.frametime; + endVelocity.z -= pm->ps.gravity * pml.frametime; + + // pm->ps->velocity = avg(pm->ps->velocity.z, endVelocity.z) + //pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; + pm->ps.velocity.z= (pm->ps.velocity.z + endVelocity.z) * 0.5f; + + //primal_velocity[2] = endVelocity[2]; + primal_velocity.z = endVelocity.z; + + if ( pml.groundPlane ) + // slide along the ground plane + //PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); + PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP); + } + + time_left = pml.frametime; + + // never turn against the ground plane + if ( pml.groundPlane ) + { + numplanes = 1; + + // planes[0] = pml.groundTrace.plane.normal + //VectorCopy( pml.groundTrace.plane.normal, planes[0] ); + planes[0] = pml.groundTrace.planenormal; + } else + numplanes = 0; + + // never turn against original velocity + VectorNormalize2( pm->ps.velocity, planes[numplanes] ); + numplanes++; + + for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) + { + + // calculate position we are trying to move to + //VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); + end = pm->ps.origin + pm->ps.velocity * time_left; + + // see if we can make it there + //pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); + //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&(end), *(const D3DXVECTOR3* const)&(pm->ps.velocity), 0, pml.traceObj); + newtrace(&trace, pm->ps.origin, end, halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + + if (trace.allsolid) + { + // entity is completely trapped in another solid + //pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration + pm->ps.velocity.z = 0; + return true; + } + + if (trace.fraction > 0) + // actually covered some distance + //VectorCopy (trace.endpos, pm->ps->origin); + pm->ps.origin = trace.endpos; + + if (trace.fraction == 1) + break; // moved the entire distance + + // save entity for contact8 + //PM_AddTouchEnt( trace.entityNum ); + + time_left -= time_left * trace.fraction; + + if (numplanes >= MAX_CLIP_PLANES) + { + // this shouldn't really happen + //VectorClear( pm->ps->velocity ); + pm->ps.velocity = Ogre::Vector3(0,0,0); + return true; + } + + // + // if this is the same plane we hit before, nudge velocity + // out along it, which fixes some epsilon issues with + // non-axial planes + // + for ( i = 0 ; i < numplanes ; i++ ) + { + if (trace.planenormal.dotProduct(planes[i]) > 0.99) //OGRE::VECTOR3 ? + //if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) + { + // pm->ps->velocity += (trace.plane.normal + pm->ps->velocity) + //VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); + pm->ps.velocity = (trace.planenormal + pm->ps.velocity); + break; + } + } + + if ( i < numplanes ) + continue; + + //VectorCopy (trace.plane.normal, planes[numplanes]); + planes[numplanes] = trace.planenormal; + numplanes++; + + // + // modify velocity so it parallels all of the clip planes + // + + // find a plane that it enters + for ( i = 0 ; i < numplanes ; i++ ) + { + //into = DotProduct( pm->ps->velocity, planes[i] ); + into = pm->ps.velocity.dotProduct(planes[i]); + if ( into >= 0.1 ) + continue; // move doesn't interact with the plane + + // see how hard we are hitting things + if ( -into > pml.impactSpeed ) + pml.impactSpeed = -into; + + // slide along the plane + //PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); + PM_ClipVelocity(pm->ps.velocity, planes[i], clipVelocity, OVERCLIP); + + // slide along the plane + PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); + + // see if there is a second plane that the new move enters + for ( j = 0 ; j < numplanes ; j++ ) + { + if ( j == i ) + continue; + + if (clipVelocity.dotProduct(planes[j]) >= 0.1) + //if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) + continue; // move doesn't interact with the plane + + // try clipping the move to the plane + PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); + PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); + + // see if it goes back into the first clip plane + if (clipVelocity.dotProduct(planes[i]) >= 0) + //if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) + continue; + + + // slide the original velocity along the crease + //dProduct (planes[i], planes[j], dir); + dir = planes[i].crossProduct(planes[j]) ; + + //VectorNormalize( dir ); + //D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir); + VectorNormalize(dir); + + //d = DotProduct( dir, pm->ps->velocity ); + d = dir.dotProduct(pm->ps.velocity); + + //VectorScale( dir, d, clipVelocity ); + clipVelocity = dir * d; + + //CrossProduct (planes[i], planes[j], dir); + dir = planes[i].crossProduct(planes[j]) ; + + + //VectorNormalize( dir ); + //D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir); + VectorNormalize(dir); + + //d = DotProduct( dir, endVelocity ); + d = dir.dotProduct(endVelocity); + + //VectorScale( dir, d, endClipVelocity ); + endClipVelocity = dir * d; + + // see if there is a third plane the the new move enters + for ( k = 0 ; k < numplanes ; k++ ) + { + if ( k == i || k == j ) + continue; + + if (clipVelocity.dotProduct(planes[k]) >= 0.1) + //if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) + continue; // move doesn't interact with the plane + + // stop dead at a tripple plane interaction + //VectorClear( pm->ps->velocity ); + //printf("Stop dead at a triple plane interaction\n"); + pm->ps.velocity = Ogre::Vector3(0,0,0); + return true; + } + } + + // if we have fixed all interactions, try another move + //VectorCopy( clipVelocity, pm->ps->velocity ); + pm->ps.velocity = clipVelocity; + + //VectorCopy( endClipVelocity, endVelocity ); + endVelocity = endClipVelocity; + break; + } + } + + if ( gravity ) + //VectorCopy( endVelocity, pm->ps->velocity ); + pm->ps.velocity = endVelocity; + + // don't change velocity if in a timer (FIXME: is this correct?) + if ( pm->ps.pm_time ) + //VectorCopy( primal_velocity, pm->ps->velocity ); + pm->ps.velocity = primal_velocity; + + //return ( (qboolean)(bumpcount != 0) ); + return bumpcount != 0; +} + +/* +================== +PM_StepSlideMove + +================== +*/ +int PM_StepSlideMove( bool gravity ) +{ + Ogre::Vector3 start_o, start_v; + Ogre::Vector3 down_o, down_v; + traceResults trace; +// float down_dist, up_dist; +// vec3_t delta, delta2; + Ogre::Vector3 up, down; + float stepSize; + + //std::cout << "StepSlideMove\n"; + // start_o = pm->ps->origin + //VectorCopy (pm->ps->origin, start_o); + start_o = pm->ps.origin; + + // start_v = pm->ps->velocity + //VectorCopy (pm->ps->velocity, start_v); + start_v = pm->ps.velocity; + + if ( PM_SlideMove( gravity ) == false ) + return 1; // we got exactly where we wanted to go first try + + + // down = start_o - vec3(0, 0, STEPSIZE) + //VectorCopy(start_o, down); + down = start_o; + down.z -= STEPSIZE; + + //pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); + //tracefunc(&trace, start_o, down, , 0, pml.scene); + //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); + newtrace(&trace, start_o, down, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + + // up = vec3(0, 0, 1) + //VectorSet(up, 0, 0, 1); + up = Ogre::Vector3(0.0f, 0.0f, 1.0f); + + // never step up when you still have up velocity + //if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) + if (pm->ps.velocity.z > 0 && ( + trace.fraction == 1.0 || trace.planenormal.dotProduct(up) < 0.7 + ) ) + return 2; + + // down_o = pm->ps->origin + //VectorCopy (pm->ps->origin, down_o); + down_o = pm->ps.origin; + + // down_v = pm->ps->velocity + //VectorCopy (pm->ps->velocity, down_v); + down_v = pm->ps.velocity; + + // up = start_o + vec3(0, 0, STEPSIZE) + //VectorCopy (start_o, up); + up = start_o; + //up[2] += STEPSIZE; + up.z += STEPSIZE; + + // test the player position if they were a stepheight higher + //pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); + //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&up, D3DXVECTOR3(0.0f, STEPSIZE, 0.0f), 0, pml.traceObj); + newtrace(&trace, start_o, up, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + if ( trace.allsolid ) + { + //if ( pm->debugLevel ) + //Com_Printf("%i:bend can't step\n", c_pmove); + //bprintf("bend can't step\n"); + return 3; // can't step up + } + + //stepSize = trace.endpos[2] - start_o[2]; + stepSize = trace.endpos.z - start_o.z; + + // try slidemove from this position + //VectorCopy (trace.endpos, pm->ps->origin); // pm->ps->origin = trace.endpos + pm->ps.origin = trace.endpos; + //VectorCopy (start_v, pm->ps->velocity); // pm->ps->velocity = start_v + pm->ps.velocity = start_v; + + PM_SlideMove( gravity ); + + // push down the final amount + + // down = pm->ps->origin - vec3(0, 0, stepSize) + //VectorCopy (pm->ps->origin, down); + down = pm->ps.origin; + //down[2] -= stepSize; + down.z -= stepSize; + + + //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); + //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); + newtrace(&trace, pm->ps.origin, down, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + if ( !trace.allsolid ) + //VectorCopy (trace.endpos, pm->ps->origin); + pm->ps.origin = trace.endpos; + + if ( trace.fraction < 1.0 ) + //PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); + PM_ClipVelocity(pm->ps.velocity, trace.planenormal, pm->ps.velocity, OVERCLIP); + + { + // use the step move + float delta; + + //delta = pm->ps->origin[2] - start_o[2]; + delta = pm->ps.origin.z - start_o.z; + if ( delta > 2 ) + { + pm->ps.counter = 10; + + /* + if (gravity) + printf("g on: %f ", delta); + else + printf("g off: %f ", delta); + + if ( delta < 7 ) + printf("stepped 3 < x < 7\n"); + //PM_AddEvent( EV_STEP_4 ); + else if ( delta < 11 ) + printf("stepped 7 < x < 11\n"); + //PM_AddEvent( EV_STEP_8 ); + else if ( delta < 15 ) + printf("stepped 11 < x < 15\n"); + //PM_AddEvent( EV_STEP_12 ); + else + printf("stepped 15+\n"); + //PM_AddEvent( EV_STEP_16 ); + */ + } + /*if ( pm->debugLevel ) + Com_Printf("%i:stepped\n", c_pmove);*/ + } + + return 4; +} + +void PM_Friction(void) +{ + + Ogre::Vector3 vec; + float* vel; + float speed, newspeed, control; + float drop; + + vel = &(pm->ps.velocity.x); + + // vec = vel + //VectorCopy( vel, vec ); + vec = pm->ps.velocity; + + if ( pml.walking ) + //vec[2] = 0; // ignore slope movement + vec.z = 0; + + //speed = VectorLength(vec); + speed = vec.length(); + if (speed < 1) + { + vel[0] = 0; + vel[1] = 0; // allow sinking underwater + // FIXME: still have z friction underwater? + //bprintf("Static friction (vec = [%f, %f, %f]) (vec.length = %f)\n", vec.x, vec.y, vec.z, speed); + return; + } + + drop = 0; + + // apply ground friction + if ( pm->ps.waterlevel <= 1 ) + { + if ( pml.walking )//&& !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) + { + // if getting knocked back, no friction + //if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) + { + control = (speed < pm_stopspeed) ? pm_stopspeed : speed; + drop += control * pm_friction * pml.frametime; + } + } + } + + // apply water friction even if just wading + if ( pm->ps.waterlevel ) + drop += speed * pm_waterfriction * pm->ps.waterlevel * pml.frametime; + + // apply flying friction + /*if ( pm->ps->powerups[PW_FLIGHT]) + drop += speed * pm_flightfriction * pml.frametime; + + if ( pm->ps->pm_type == PM_SPECTATOR) + drop += speed * pm_spectatorfriction * pml.frametime;*/ + if (pm->ps.move_type == PM_SPECTATOR) + drop += speed * pm_flightfriction * pml.frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + + newspeed /= speed; + + // vel *= newspeed + vel[0] = vel[0] * newspeed; + vel[1] = vel[1] * newspeed; + vel[2] = vel[2] * newspeed; +} + +float PM_CmdScale(playerMove::playercmd* const cmd) +{ + int max; + float total; + float scale; + + max = abs( cmd->forwardmove ); + if ( abs( cmd->rightmove ) > max ) + max = abs( cmd->rightmove ); + + if ( abs( cmd->upmove ) > max ) + max = abs( cmd->upmove ); + + if ( !max ) + return 0; + + total = sqrtf( (const float)(cmd->forwardmove * cmd->forwardmove + + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove) ); + scale = (float)pm->ps.speed * max / ( 127.0f * total ); + if(pm->ps.move_type == PM_NOCLIP) + scale *= 10; + + return scale; +} + +static void PM_Accelerate( Ogre::Vector3& wishdir, float wishspeed, float accel ) +{ +// int i; + float addspeed, accelspeed, currentspeed; + + // currentspeed = pm->ps->velocity dot wishdir + //currentspeed = DotProduct (pm->ps->velocity, wishdir); + currentspeed = pm->ps.velocity.dotProduct(wishdir); + + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + + accelspeed = accel * pml.frametime * wishspeed; + + // Clamp accelspeed at addspeed + if (accelspeed > addspeed) + accelspeed = addspeed; + + // pm->ps->velocity += accelspeed * wishdir + //for (i=0 ; i<3 ; i++) + //pm->ps->velocity[i] += accelspeed * wishdir[i]; + pm->ps.velocity += (wishdir * accelspeed); +} + +static bool PM_CheckJump(void) +{ + //if ( pm->ps->pm_flags & PMF_RESPAWNED ) + //return qfalse; // don't allow jump until all buttons are up + + if ( pm->cmd.upmove < 10 ) + // not holding jump + return false; + + pm->cmd.upmove = 0; + + // must wait for jump to be released + /*if ( pm->ps->pm_flags & PMF_JUMP_HELD ) + { + // clear upmove so cmdscale doesn't lower running speed + pm->cmd.upmove = 0; + return false; + }*/ + + pml.groundPlane = false; // jumping away + pml.walking = false; + //pm->ps->pm_flags |= PMF_JUMP_HELD; + + pm->ps.groundEntityNum = ENTITYNUM_NONE; + pm->ps.velocity.z = JUMP_VELOCITY; + pm->ps.bSnap = false; + //PM_AddEvent( EV_JUMP ); + + /*if ( pm->cmd.forwardmove >= 0 ) + { + PM_ForceLegsAnim( LEGS_JUMP ); + pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; + } + else + { + PM_ForceLegsAnim( LEGS_JUMPB ); + pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; + }*/ + + return true; +} + +static void PM_WaterMove( playerMove* const pm ) +{ + //int i; + //vec3_t wishvel; + Ogre::Vector3 wishvel; + float wishspeed; + //vec3_t wishdir; + Ogre::Vector3 wishdir; + float scale; + float vel; + + pm->ps.bSnap = false; + + /*if ( PM_CheckWaterJump() ) + { + PM_WaterJumpMove(); + return; + }*/ +#if 0 + // jump = head for surface + if ( pm->cmd.upmove >= 10 ) { + if (pm->ps->velocity[2] > -300) { + if ( pm->watertype == CONTENTS_WATER ) { + pm->ps->velocity[2] = 100; + } else if (pm->watertype == CONTENTS_SLIME) { + pm->ps->velocity[2] = 80; + } else { + pm->ps->velocity[2] = 50; + } + } + } +#endif + PM_Friction (); + + if (pm->cmd.forwardmove || pm->cmd.rightmove) + { + //NEEDS TO BE REWRITTEN FOR OGRE TIME--------------------------------------------------- + /* + static const TimeTicks footstep_duration = GetTimeFreq(); // make each splash last 1.0s + static TimeTicks lastStepTime = 0; + const TimeTicks thisStepTime = GetTimeQPC(); + static bool lastWasLeft = false; + if (thisStepTime > lastStepTime) + { + if (pm->cmd.ducking) + lastStepTime = thisStepTime + footstep_duration * 2; // splashes while ducking are twice as slow + else + lastStepTime = thisStepTime + footstep_duration; + + lastWasLeft = !lastWasLeft; + */ + //-----------------jhooks1 + + /* + namestruct defaultCreature; + const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_swim : SNDG::l_swim); + if (sndg) + { + const namestruct& SOUNID = sndg->soundID; + const SOUN* const soun = SOUN::GetSound(SOUNID); + if (soun) + { + PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); + } + }*/ + //Sound, ignore for now -- jhooks1 + //} + } + + scale = PM_CmdScale( &pm->cmd ); + // + // user intentions + // + if ( !scale ) + { + /*wishvel[0] = 0; + wishvel[1] = 0; + wishvel[2] = -60; // sink towards bottom + */ + wishvel.x = 0; + wishvel.z = -60; + wishvel.y = 0; + } + else + { + /*for (i=0 ; i<3 ; i++) + wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;*/ + wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; + + //wishvel[2] += scale * pm->cmd.upmove; + wishvel.z += pm->cmd.upmove * scale; + } + + //VectorCopy (wishvel, wishdir); + wishdir = wishvel; + wishspeed = VectorNormalize(wishdir); + + if ( wishspeed > pm->ps.speed * pm_swimScale ) + wishspeed = pm->ps.speed * pm_swimScale; + + PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); + + // make sure we can go up slopes easily under water + //if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) + if (pml.groundPlane && pm->ps.velocity.dotProduct(pml.groundTrace.planenormal) < 0.0f) + { + //vel = VectorLength(pm->ps->velocity); + vel = pm->ps.velocity.length(); + + // slide along the ground plane + //PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); + PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP); + + VectorNormalize(pm->ps.velocity); + //VectorScale(pm->ps->velocity, vel, pm->ps->velocity); + pm->ps.velocity = pm->ps.velocity * vel; + } + + PM_SlideMove( false ); +} + +/* +=================== +PM_WalkMove + +=================== +*/ +static void PM_WalkMove( playerMove* const pmove ) +{ +// int i; + Ogre::Vector3 wishvel; + float fmove, smove; + Ogre::Vector3 wishdir; + float wishspeed; + float scale; + playerMove::playercmd cmd; + float accelerate; + float vel; + //pm->ps.gravity = 4000; + + if ( pm->ps.waterlevel > 2 && //DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) + pml.forward.dotProduct(pml.groundTrace.planenormal) > 0.0f) + { + // begin swimming + PM_WaterMove(pmove); + return; + } + + + if ( PM_CheckJump () ) + { + + // jumped away + if ( pm->ps.waterlevel > 1 ) + PM_WaterMove(pmove); + else + PM_AirMove(); + //printf("Jumped away\n"); + return; + } + + // Footsteps time + if (pmove->cmd.forwardmove || pmove->cmd.rightmove) + { + bool step_underwater = false; + //if (pmove->traceObj) + //{ + + + //jhooks1 - Water handling, deal with later + + + + if (pmove->hasWater) + { + if (pmove->hasWater ) + { + const float waterHeight = pmove->waterHeight; + const float waterSoundStepHeight = waterHeight + halfExtents.y; + if (pmove->ps.origin.y < waterSoundStepHeight) + step_underwater = true; + } + } + //} + + /* + static const TimeTicks footstep_duration = GetTimeFreq() / 2; // make each footstep last 500ms + static TimeTicks lastStepTime = 0; + const TimeTicks thisStepTime = GetTimeQPC(); + static bool lastWasLeft = false; + if (thisStepTime > lastStepTime) + { + if (pmove->cmd.ducking) + lastStepTime = thisStepTime + footstep_duration * 2; // footsteps while ducking are twice as slow + else + lastStepTime = thisStepTime + footstep_duration; + + lastWasLeft = !lastWasLeft; + */ + + if (step_underwater) + { + /* + const namestruct ns(lastWasLeft ? "FootWaterRight" : "FootWaterLeft"); + const SOUN* const soun = SOUN::GetSound(ns); + if (soun) + { + PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); + }*/ + } + else + { + /* + namestruct defaultCreature; + const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_foot : SNDG::l_foot); + if (sndg) + { + const namestruct& SOUNID = sndg->soundID; + const SOUN* const soun = SOUN::GetSound(SOUNID); + if (soun) + { + PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); + } + }*/ + } + } + + + PM_Friction (); + + + //bprintf("vel: (%f, %f, %f)\n", pm->ps.velocity.x, pm->ps.velocity.y, pm->ps.velocity.z); + + fmove = pm->cmd.forwardmove; + smove = pm->cmd.rightmove; + + + cmd = pm->cmd; + scale = PM_CmdScale( &cmd ); + + // set the movementDir so clients can rotate the legs for strafing + //PM_SetMovementDir(); + + // project moves down to flat plane + //pml.forward[2] = 0; + pml.forward.z = 0; + + //pml.right[2] = 0; + pml.right.z = 0; + //std::cout << "Further down" << pm->ps.velocity << "\n"; + + + // project the forward and right directions onto the ground plane + PM_ClipVelocity (pml.forward, pml.groundTrace.planenormal, pml.forward, OVERCLIP ); + PM_ClipVelocity (pml.right, pml.groundTrace.planenormal, pml.right, OVERCLIP ); + //std::cout << "Clip velocity" << pm->ps.velocity << "\n"; + // + + VectorNormalize (pml.forward); + VectorNormalize (pml.right); + //pml.forward = pml.forward.normalise(); + //pml.right = pml.right.normalise(); + //std::cout << "forward2" << pml.forward << "\n"; + //std::cout << "right2" << pml.right << "\n"; + + + // wishvel = (pml.forward * fmove) + (pml.right * smove); + //for ( i = 0 ; i < 3 ; i++ ) + //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; + wishvel = pml.forward * fmove + pml.right * smove; + + + //bprintf("f: (%f, %f, %f), s: (%f, %f, %f)\n", fmove, smove); + + + // when going up or down slopes the wish velocity should Not be zero +// wishvel[2] = 0; + + // wishdir = wishvel + //VectorCopy (wishvel, wishdir); + //wishvel = wishdir; + wishdir = wishvel; + + wishspeed = VectorNormalize(wishdir); + //std::cout << "Wishspeed: " << wishspeed << "\n"; + wishspeed *= scale; + //std::cout << "Wishspeed scaled:" << wishspeed << "\n"; + + // clamp the speed lower if ducking + if ( pm->cmd.ducking ) + if ( wishspeed > pm->ps.speed * pm_duckScale ) + wishspeed = pm->ps.speed * pm_duckScale; + + // clamp the speed lower if wading or walking on the bottom + if ( pm->ps.waterlevel ) + { + float waterScale; + + waterScale = pm->ps.waterlevel / 3.0f; + waterScale = 1.0f - ( 1.0f - pm_swimScale ) * waterScale; + if ( wishspeed > pm->ps.speed * waterScale ) + wishspeed = pm->ps.speed * waterScale; + } + + // when a player gets hit, they temporarily lose + // full control, which allows them to be moved a bit + //if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) + //accelerate = pm_airaccelerate; + //else + accelerate = pm_accelerate; + + + PM_Accelerate (wishdir, wishspeed, accelerate); + //std::cout << "Velocityafter: " << pm->ps.velocity << "\n"; + + //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); + //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); + + //if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) + //pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; + //else + //{ + // don't reset the z velocity for slopes +// pm->ps->velocity[2] = 0; + //} + + //vel = VectorLength(pm->ps->velocity); + vel = pm->ps.velocity.length(); + //std::cout << "The length" << vel << "\n"; + + // slide along the ground plane + PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, + pm->ps.velocity, OVERCLIP ); + //std::cout << "Velocity clipped" << pm->ps.velocity << "\n"; + + // don't decrease velocity when going up or down a slope + VectorNormalize(pm->ps.velocity); + //pm->ps.velocity = pm->ps.velocity.normalise(); + + //std::cout << "Final:" << pm->ps.velocity << "\n"; + //VectorScale(pm->ps->velocity, vel, pm->ps->velocity); + pm->ps.velocity = pm->ps.velocity * vel; + + // don't do anything if standing still + //if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) + if (!pm->ps.velocity.x && !pm->ps.velocity.z) + return; + + PM_StepSlideMove( false ); + + //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); + + +} + +void PM_UpdateViewAngles( playerMove::playerStruct* const ps, playerMove::playercmd* const cmd ) +{ + short temp; + int i; + + //while(1); + + //if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) + //return; // no view changes at all + + //if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) + //return; // no view changes at all + + // circularly clamp the angles with deltas + //bprintf("View angles: %i, %i, %i\n", cmd->angles[0], cmd->angles[1], cmd->angles[2]); + for (i = 0 ; i < 3 ; i++) + { + temp = cmd->angles[i];// + ps->delta_angles[i]; + //if ( i == PITCH ) + { + // don't let the player look up or down more than 90 degrees + /*if ( temp > 16000 ) + { + ps->delta_angles[i] = 16000 - cmd->angles[i]; + temp = 16000; + } + else if ( temp < -16000 ) + { + ps->delta_angles[i] = -16000 - cmd->angles[i]; + temp = -16000; + }*/ + } + (&(ps->viewangles.x) )[i] = SHORT2ANGLE(temp); + //cmd->angles[i] += ps->delta_angles[i]; + } + //ps->delta_angles[0] = ps->delta_angles[1] = ps->delta_angles[2] = 0; + +} + +void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) +{ + float angle; + static float sr, sp, sy, cr, cp, cy; + // static to help MS compiler fp bugs + + //angle = angles[YAW] * (M_PI*2 / 360); + angle = angles.x * (M_PI * 2.0f / 360.0f); + sp = sinf(angle); + cp = cosf(angle); + + //angle = angles[PITCH] * (M_PI*2 / 360); + angle = angles.y * (-M_PI * 2.0f / 360.0f); + sy = sinf(angle); + cy = cosf(angle); + + //angle = angles[ROLL] * (M_PI*2 / 360); + angle = angles.z * (M_PI * 2.0f / 360.0f); + sr = sinf(angle); + cr = cosf(angle); + + if (forward) + { + forward->x = cp * cy; + forward->y = cp * sy; + forward->z = -sp; + } + if (right) + { + right->x = (-1 * sr * sp * cy + -1 * cr * -sy); + right->y = (-1 * sr * sp * sy + -1 * cr * cy); + right->z = 0; + } + if (up) + { + up->x =(cr * sp * cy + -sr * -sy); + up->y=(cr * sp * sy + -sr * cy); + up->z = cr * cp; + } + +} + +void PM_GroundTraceMissed() +{ + traceResults trace; + Ogre::Vector3 point; + //std::cout << "Ground trace missed\n"; + // we just transitioned into freefall + //if ( pm->debugLevel ) + //Com_Printf("%i:lift\n", c_pmove); + + // if they aren't in a jumping animation and the ground is a ways away, force into it + // if we didn't do the trace, the player would be backflipping down staircases + //VectorCopy( pm->ps->origin, point ); + point = pm->ps.origin; + //point[2] -= 64; + point.z -= 32; + + //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -64.0f, 0.0f), 0, pml.traceObj); + newtrace(&trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + //It hit the ground below + if ( trace.fraction < 1.0 && pm->ps.origin.z > trace.endpos.z) + { + pm->ps.origin = trace.endpos; + pml.walking = true; + pml.groundPlane = true; + pm->ps.groundEntityNum = trace.entityNum; + + } + else{ + pm->ps.groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = false; + pml.walking = false; + pm->ps.bSnap = false; + } + + +} + +static bool PM_CorrectAllSolid(traceResults* const trace) +{ + int i, j, k; + Ogre::Vector3 point; + + //if ( pm->debugLevel ) + //Com_Printf("%i:allsolid\n", c_pmove); + //bprintf("allsolid\n"); + + // jitter around + for (i = -1; i <= 1; i++) + { + for (j = -1; j <= 1; j++) + { + for (k = -1; k <= 1; k++) + { + //VectorCopy(pm->ps->origin, point); + point = pm->ps.origin; + + /*point[0] += (float) i; + point[1] += (float) j; + point[2] += (float) k;*/ + point += Ogre::Vector3( (const float)i, (const float)j, (const float)k); + + //pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + //tracefunc(trace, *(const D3DXVECTOR3* const)&point, *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, 0.0f, 0.0f), 0, pml.traceObj); + newtrace(trace, point, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + + if ( !trace->allsolid ) + { + /*point[0] = pm->ps->origin[0]; + point[1] = pm->ps->origin[1]; + point[2] = pm->ps->origin[2] - 0.25;*/ + point = pm->ps.origin; + point.z -= 0.25f; + + //pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + //tracefunc(trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); + newtrace(trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + pml.groundTrace = *trace; + return true; + } + } + } + } + + //pm->ps->groundEntityNum = ENTITYNUM_NONE; + pm->ps.groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = false; + pml.walking = false; + + return false; +} + +static void PM_CrashLand( void ) +{ + float delta; + float dist ; + float vel, acc; + float t; + float a, b, c, den; + + // decide which landing animation to use + /*if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) + PM_ForceLegsAnim( LEGS_LANDB ); + else + PM_ForceLegsAnim( LEGS_LAND ); + + pm->ps->legsTimer = TIMER_LAND;*/ + + // calculate the exact velocity on landing + //dist = pm->ps->origin[2] - pml.previous_origin[2]; + + dist = pm->ps.origin.z - pml.previous_origin.z; + + //vel = pml.previous_velocity[2]; + vel = pml.previous_velocity.z; + + //acc = -pm->ps->gravity; + acc = -pm->ps.gravity; + + a = acc / 2; + b = vel; + c = -dist; + + den = b * b - 4 * a * c; + if ( den < 0 ) + return; + + t = (-b - sqrtf( den ) ) / ( 2 * a ); + + delta = vel + t * acc; + delta = delta * delta * 0.0001f; + + // ducking while falling doubles damage + /*if ( pm->ps->pm_flags & PMF_DUCKED ) + delta *= 2;*/ + if (pm->cmd.upmove < -20) + delta *= 2; + + // never take falling damage if completely underwater + if ( pm->ps.waterlevel == 3 ) + return; + + // reduce falling damage if there is standing water + if ( pm->ps.waterlevel == 2 ) + delta *= 0.25; + if ( pm->ps.waterlevel == 1 ) + delta *= 0.5; + + if ( delta < 1 ) + return; +/* + if (delta > 60) + printf("Far crashland: %f\n", delta); + else if (delta > 40) + printf("Medium crashland: %f\n", delta); + else if (delta > 4) + printf("Short crashland: %f\n", delta); +*/ + if (delta > 60) + { + /* + static const namestruct healthDamage("Health Damage"); + const SOUN* const soun = SOUN::GetSound(healthDamage); + if (soun) + { + PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); + }*/ + } + + if (delta > 3) // We need at least a short crashland to proc the sound effects: + { + bool splashSound = false; + + if (pm->hasWater) + { + + const float waterHeight = pm->waterHeight; + const float waterHeightSplash = waterHeight + halfExtents.y; + if (pm->ps.origin.z < waterHeightSplash) + { + splashSound = true; + } + + } + + + if (splashSound) + { + //Change this later----------------------------------- + /* + const namestruct ns("DefaultLandWater"); + const SOUN* const soun = SOUN::GetSound(ns); + if (soun) + { + PlaySound2D(soun->soundFilename, soun->soundDatga->GetVolumeFloat() ); + }*/ + } + else + { + //Change this later--------------------------------- + /* + namestruct defaultCreature; + const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, SNDG::land); + if (sndg) + { + const namestruct& SOUNID = sndg->soundID; + const SOUN* const soun = SOUN::GetSound(SOUNID); + if (soun) + { + PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); + } + }*/ + } + } + + // create a local entity event to play the sound + + // SURF_NODAMAGE is used for bounce pads where you don't ever + // want to take damage or play a crunch sound + //if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) + { + /*if ( delta > 60 ) + PM_AddEvent( EV_FALL_FAR ); + else if ( delta > 40 ) + { + // this is a pain grunt, so don't play it if dead + if ( pm->ps->stats[STAT_HEALTH] > 0 ) + PM_AddEvent( EV_FALL_MEDIUM ); + } + else if ( delta > 7 ) + PM_AddEvent( EV_FALL_SHORT ); + else + PM_AddEvent( PM_FootstepForSurface() );*/ + } + + // start footstep cycle over + //pm->ps->bobCycle = 0; +} + +static void PM_GroundTrace( void ) +{ + Ogre::Vector3 point; + traceResults trace; + + /*point[0] = pm->ps->origin[0]; + point[1] = pm->ps->origin[1]; + point[2] = pm->ps->origin[2] - 0.25;*/ + point = pm->ps.origin; + point.z -= 0.25f; + + //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); + newtrace(&trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + pml.groundTrace = trace; + + // do something corrective if the trace starts in a solid... + if ( trace.allsolid ) { + //std::cout << "ALL SOLID\n"; + if ( !PM_CorrectAllSolid(&trace) ){ + //std::cout << "Returning after correct all solid\n"; + return; + } + } + // if the trace didn't hit anything, we are in free fall + if ( trace.fraction == 1.0) + { + if(pm->ps.snappingImplemented){ + if(pm->ps.bSnap && pm->ps.counter <= 0) + PM_GroundTraceMissed(); + } + + + return; + } + else + { + //It hit something, so we are on the ground + pm->ps.bSnap = true; + + } + // check if getting thrown off the ground + //if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) + if (pm->ps.velocity.z > 0 && pm->ps.velocity.dotProduct(trace.planenormal) > 10.0f ) + { + //if ( pm->debugLevel ) + //Com_Printf("%i:kickoff\n", c_pmove); + + // go into jump animation + /*if ( pm->cmd.forwardmove >= 0 ) + { + PM_ForceLegsAnim( LEGS_JUMP ); + pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; + } + else + { + PM_ForceLegsAnim( LEGS_JUMPB ); + pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; + }*/ + if(!pm->ps.bSnap){ + pm->ps.groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = false; + pml.walking = false; + } + else + { + pml.groundPlane = true; + pml.walking = true; + } + return; + } + + + + + // slopes that are too steep will not be considered onground + //if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) + if (trace.planenormal.z < MIN_WALK_NORMAL) + { + //if ( pm->debugLevel ) + //Com_Printf("%i:steep\n", c_pmove); + + // FIXME: if they can't slide down the slope, let them + // walk (sharp crevices) + pm->ps.groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = true; + pml.walking = false; + return; + } + + pml.groundPlane = true; + pml.walking = true; + + // hitting solid ground will end a waterjump + /*if (pm->ps.pm_flags & PMF_TIME_WATERJUMP) + { + pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); + pm->ps->pm_time = 0; + }*/ + + if ( pm->ps.groundEntityNum == ENTITYNUM_NONE ) + { + // just hit the ground + /*if ( pm->debugLevel ) + Com_Printf("%i:Land\n", c_pmove);*/ + //bprintf("Land\n"); + + PM_CrashLand(); + + // don't do landing time if we were just going down a slope + //if ( pml.previous_velocity[2] < -200 ) + if (pml.previous_velocity.z < -200) + { + // don't allow another jump for a little while + //pm->ps->pm_flags |= PMF_TIME_LAND; + pm->ps.pm_time = 250; + } + } + + pm->ps.groundEntityNum = trace.entityNum; + + // don't reset the z velocity for slopes +// pm->ps->velocity[2] = 0; + + //PM_AddTouchEnt( trace.entityNum ); +} + +void PM_AirMove() +{ + //int i; + Ogre::Vector3 wishvel; + float fmove, smove; + Ogre::Vector3 wishdir; + float wishspeed; + float scale; + playerMove::playercmd cmd; + //pm->ps.gravity = 800; + PM_Friction(); + + fmove = pm->cmd.forwardmove; + smove = pm->cmd.rightmove; + + cmd = pm->cmd; + scale = PM_CmdScale( &cmd ); + // set the movementDir so clients can rotate the legs for strafing + //PM_SetMovementDir(); + + // project moves down to flat plane + //pml.forward[2] = 0; + pml.forward.z = 0; //Z or Y? + //pml.right[2] = 0; + pml.right.z = 0; + //VectorNormalize (pml.forward); + VectorNormalize(pml.forward); + VectorNormalize(pml.right); + //VectorNormalize (pml.right); + + //for ( i = 0 ; i < 2 ; i++ ) + //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; + wishvel = pml.forward * fmove + pml.right * smove; + + //wishvel[2] = 0; + wishvel.z = 0; + + //VectorCopy (wishvel, wishdir); + wishdir = wishvel; + //wishspeed = VectorNormalize(wishdir); + wishspeed = VectorNormalize(wishdir); + + wishspeed *= scale; + + // not on ground, so little effect on velocity + PM_Accelerate (wishdir, wishspeed, pm_airaccelerate); + + // we may have a ground plane that is very steep, even + // though we don't have a groundentity + // slide along the steep plane + if ( pml.groundPlane ) + PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP ); + +/*#if 0 + //ZOID: If we are on the grapple, try stair-stepping + //this allows a player to use the grapple to pull himself + //over a ledge + if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) + PM_StepSlideMove ( qtrue ); + else + PM_SlideMove ( qtrue ); +#endif*/ + + /*bprintf("%i ", */PM_StepSlideMove ( true )/* )*/; +} + +static void PM_NoclipMove( void ) +{ + float speed, drop, friction, control, newspeed; +// int i; + Ogre::Vector3 wishvel; + float fmove, smove; + Ogre::Vector3 wishdir; + float wishspeed; + float scale; + + //pm->ps->viewheight = DEFAULT_VIEWHEIGHT; + + // friction + + //speed = VectorLength (pm->ps->velocity); + speed = pm->ps.velocity.length(); + if (speed < 1) + //VectorCopy (vec3_origin, pm->ps->velocity); + pm->ps.velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); + else + { + drop = 0; + + friction = pm_friction * 1.5f; // extra friction + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control * friction * pml.frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + //VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); + pm->ps.velocity = pm->ps.velocity * newspeed; + } + + // accelerate + scale = PM_CmdScale( &pm->cmd ); + + fmove = pm->cmd.forwardmove; + smove = pm->cmd.rightmove; + + //for (i=0 ; i<3 ; i++) + //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; + + wishvel = pml.forward * fmove + pml.right * smove; + //wishvel[2] += pm->cmd.upmove; + wishvel.z += pm->cmd.upmove; + + //VectorCopy (wishvel, wishdir); + wishdir = wishvel; + wishspeed = VectorNormalize(wishdir); + wishspeed *= scale; + + + PM_Accelerate( wishdir, wishspeed, pm_accelerate ); + + // move + //VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); + pm->ps.origin = pm->ps.origin + pm->ps.velocity * pml.frametime; +} + +static void PM_DropTimers( void ) +{ + // drop misc timing counter + if ( pm->ps.pm_time ) + { + if ( pml.msec >= pm->ps.pm_time ) + { + //pm->ps->pm_flags &= ~PMF_ALL_TIMES; + pm->ps.pm_time = 0; + } + else + pm->ps.pm_time -= pml.msec; + } + + //bprintf("Time: %i\n", pm->ps.pm_time); + + // drop animation counter + /*if ( pm->ps->legsTimer > 0 ) + { + pm->ps->legsTimer -= pml.msec; + if ( pm->ps->legsTimer < 0 ) + pm->ps->legsTimer = 0; + } + + if ( pm->ps->torsoTimer > 0 ) + { + pm->ps->torsoTimer -= pml.msec; + if ( pm->ps->torsoTimer < 0 ) + pm->ps->torsoTimer = 0; + }*/ +} + +static void PM_FlyMove( void ) +{ + //int i; + Ogre::Vector3 wishvel; + float wishspeed; + Ogre::Vector3 wishdir; + float scale; + + // normal slowdown + PM_Friction (); + + scale = PM_CmdScale( &pm->cmd ); + // + // user intentions + // + if ( !scale ) + { + /*wishvel[0] = 0; + wishvel[1] = 0; + wishvel[2] = 0;*/ + wishvel = Ogre::Vector3(0,0,0); + } + else + { + //for (i=0 ; i<3 ; i++) + //wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; + wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; + + //wishvel[2] += scale * pm->cmd.upmove; + wishvel.z += /*6.35f * */pm->cmd.upmove * scale; + } + + //VectorCopy (wishvel, wishdir); + wishdir = wishvel; + + //wishspeed = VectorNormalize(wishdir); + wishspeed = VectorNormalize(wishdir); + + PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate); + + PM_StepSlideMove( false ); +} + + +void PM_SetWaterLevel( playerMove* const pm ) +{ + Ogre::Vector3 point; + //int cont; + int sample1; + int sample2; + + // + // get waterlevel, accounting for ducking + // + + pm->ps.waterlevel = WL_DRYLAND; + pm->ps.watertype = 0; + + /*point[0] = pm->ps->origin[0]; + point[1] = pm->ps->origin[1]; + point[2] = pm->ps->origin[2] + MINS_Z + 1; */ + point.x = pm->ps.origin.x; + point.y = pm->ps.origin.y; + point.z = pm->ps.origin.z + MINS_Z + 1; + + //cont = pm->pointcontents( point, pm->ps->clientNum ); + bool checkWater = (pml.hasWater && pml.waterHeight > point.z); + //if ( cont & MASK_WATER ) + if ( checkWater) + { + sample2 = /*pm->ps.viewheight*/DEFAULT_VIEWHEIGHT - MINS_Z; + sample1 = sample2 / 2; + + pm->ps.watertype = CONTENTS_WATER;//cont; + pm->ps.waterlevel = WL_ANKLE; + //point[2] = pm->ps->origin[2] + MINS_Z + sample1; + point.z = pm->ps.origin.z + MINS_Z + sample1; + checkWater = (pml.hasWater && pml.waterHeight > point.z); + //cont = pm->pointcontents (point, pm->ps->clientNum ); + //if ( cont & MASK_WATER ) + if (checkWater) + { + pm->ps.waterlevel = WL_WAIST; + //point[2] = pm->ps->origin[2] + MINS_Z + sample2; + point.z = pm->ps.origin.z + MINS_Z + sample2; + //cont = pm->pointcontents (point, pm->ps->clientNum ); + //if ( cont & MASK_WATER ) + checkWater = (pml.hasWater && pml.waterHeight > point.z); + if (checkWater ) + pm->ps.waterlevel = WL_UNDERWATER; + } + } +} + +void PmoveSingle (playerMove* const pmove) +{ + pmove->ps.counter--; + //pm = pmove; + + // Aedra doesn't support Q3-style VM traps D: //while(1); + + // this counter lets us debug movement problems with a journal + // by setting a conditional breakpoint fot the previous frame + //c_pmove++; + + // clear results + //pm->numtouch = 0; + pm->ps.watertype = 0; + pm->ps.waterlevel = WL_DRYLAND; + + //if ( pm->ps->stats[STAT_HEALTH] <= 0 ) + //pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies + + + // make sure walking button is clear if they are running, to avoid + // proxy no-footsteps cheats + //if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) + //pm->cmd.buttons &= ~BUTTON_WALKING; + + + // set the talk balloon flag + //if ( pm->cmd.buttons & BUTTON_TALK ) + //pm->ps->eFlags |= EF_TALK; + //else + //pm->ps->eFlags &= ~EF_TALK; + + // set the firing flag for continuous beam weapons + /*if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION + && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) + pm->ps->eFlags |= EF_FIRING; + else + pm->ps->eFlags &= ~EF_FIRING;*/ + + // clear the respawned flag if attack and use are cleared + /*if ( pm->ps->stats[STAT_HEALTH] > 0 && + !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) + pm->ps->pm_flags &= ~PMF_RESPAWNED;*/ + + // if talk button is down, dissallow all other input + // this is to prevent any possible intercept proxy from + // adding fake talk balloons + /*if ( pmove->cmd.buttons & BUTTON_TALK ) + { + // keep the talk button set tho for when the cmd.serverTime > 66 msec + // and the same cmd is used multiple times in Pmove + pmove->cmd.buttons = BUTTON_TALK; + pmove->cmd.forwardmove = 0; + pmove->cmd.rightmove = 0; + pmove->cmd.upmove = 0; + }*/ + + // clear all pmove local vars + memset (&pml, 0, sizeof(pml) ); + + // Aedra-specific code: + //pml.scene = global_lastscene; + + + // End Aedra-specific code + pml.hasWater = pmove->hasWater; + pml.isInterior = pmove->isInterior; + pml.waterHeight = pmove->waterHeight; +#ifdef _DEBUG + if (!pml.traceObj) + __debugbreak(); + + if (!pml.traceObj->incellptr) + __debugbreak(); +#endif + + // determine the time + pml.msec = pmove->cmd.serverTime - pm->ps.commandTime; + if ( pml.msec < 1 ) + pml.msec = 1; + else if ( pml.msec > 200 ) + pml.msec = 200; + + //pm->ps->commandTime = pmove->cmd.serverTime; + + // Commented out as a hack + pm->ps.commandTime = pmove->cmd.serverTime; + + // Handle state change procs: + if (pm->cmd.activating != pm->cmd.lastActivatingState) + { + if (!pm->cmd.lastActivatingState && pm->cmd.activating) + pm->cmd.procActivating = playerMove::playercmd::KEYDOWN; + else + pm->cmd.procActivating = playerMove::playercmd::KEYUP; + } + else + { + pm->cmd.procActivating = playerMove::playercmd::NO_CHANGE; + } + pm->cmd.lastActivatingState = pm->cmd.activating; + + if (pm->cmd.dropping != pm->cmd.lastDroppingState) + { + if (!pm->cmd.lastDroppingState && pm->cmd.dropping) + pm->cmd.procDropping = playerMove::playercmd::KEYDOWN; + else + pm->cmd.procDropping = playerMove::playercmd::KEYUP; + } + else + { + pm->cmd.procDropping = playerMove::playercmd::NO_CHANGE; + } + pm->cmd.lastDroppingState = pm->cmd.dropping; + + // save old org in case we get stuck + //VectorCopy (pm->ps->origin, pml.previous_origin); + pml.previous_origin = pm->ps.origin; + + // Copy over the lastframe origin + pmove->ps.lastframe_origin = pmove->ps.origin; + + //pmove->ps.lastframe_origin = pmove->ps.origin; + + // save old velocity for crashlanding + //VectorCopy (pm->ps->velocity, pml.previous_velocity); + pml.previous_velocity = pm->ps.velocity; + + pml.frametime = pml.msec * 0.001f; + + // update the viewangles + //PM_UpdateViewAngles( &(pm->ps), &(pm->cmd) ); + + AngleVectors (pm->ps.viewangles, &(pml.forward), &(pml.right), &(pml.up) ); + + //if ( pm->cmd.upmove < 10 ) + // not holding jump + //pm->ps->pm_flags &= ~PMF_JUMP_HELD; + + // decide if backpedaling animations should be used + /*if ( pm->cmd.forwardmove < 0 ) + pm->ps->pm_flags |= PMF_BACKWARDS_RUN; + else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) + pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;*/ + + /*if ( pm->ps->pm_type >= PM_DEAD ) + { + pm->cmd.forwardmove = 0; + pm->cmd.rightmove = 0; + pm->cmd.upmove = 0; + }*/ + + if ( pm->ps.move_type == PM_SPECTATOR ) + { + + //PM_CheckDuck (); + PM_FlyMove (); + PM_DropTimers (); + return; + } + + if ( pm->ps.move_type == PM_NOCLIP ) + { + + PM_NoclipMove (); + PM_DropTimers (); + return; + } + + if (pm->ps.move_type == PM_FREEZE){ + + return; // no movement at all + + } + + if ( pm->ps.move_type == PM_INTERMISSION || pm->ps.move_type == PM_SPINTERMISSION){ + return; // no movement at all + } + + // set watertype, and waterlevel + PM_SetWaterLevel(pmove); + pml.previous_waterlevel = pmove->ps.waterlevel; + + // set mins, maxs, and viewheight + //PM_CheckDuck (); + + // set groundentity + PM_GroundTrace(); + + /*if ( pm->ps->pm_type == PM_DEAD ) + PM_DeadMove (); + + PM_DropTimers();*/ + + PM_DropTimers(); + +/*#ifdef MISSIONPACK + if ( pm->ps->powerups[PW_INVULNERABILITY] ) { + PM_InvulnerabilityMove(); + } else +#endif*/ + /*if ( pm->ps->powerups[PW_FLIGHT] ) + // flight powerup doesn't allow jump and has different friction + PM_FlyMove(); + else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) + { + PM_GrappleMove(); + // We can wiggle a bit + PM_AirMove(); + } + else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) + PM_WaterJumpMove();*/ + if ( pmove->ps.waterlevel > 1 ) + // swimming + PM_WaterMove(pmove); + else if ( pml.walking ) + { + + // walking on ground + PM_WalkMove(pmove); + //bprintf("WalkMove\n"); + } + else + { + // airborne + //std::cout << "AIRMOVE\n"; + PM_AirMove(); + //bprintf("AirMove\n"); + } + + //PM_Animate(); + + // set groundentity, watertype, and waterlevel + PM_GroundTrace(); + PM_SetWaterLevel(pmove); + + // weapons + /*PM_Weapon(); + + // torso animation + PM_TorsoAnimation(); + + // footstep events / legs animations + PM_Footsteps(); + + // entering / leaving water splashes + PM_WaterEvents(); + + // snap some parts of playerstate to save network bandwidth + trap_SnapVector( pm->ps->velocity );*/ +} + +void Ext_UpdateViewAngles(playerMove* const pm) +{ + playerMove::playerStruct* const ps = &(pm->ps); + playerMove::playercmd* const cmd = &(pm->cmd); + PM_UpdateViewAngles(ps, cmd); +} + +void Pmove (playerMove* const pmove) +{ + // warning: unused variable ‘fmove’ + //int fmove = pmove->cmd.forwardmove; + + pm = pmove; + + int finalTime; + + finalTime = pmove->cmd.serverTime; + + pmove->ps.commandTime = 40; + + if ( finalTime < pmove->ps.commandTime ) + return; // should not happen + + if ( finalTime > pmove->ps.commandTime + 1000 ) + pmove->ps.commandTime = finalTime - 1000; + + pmove->ps.pmove_framecount = (pmove->ps.pmove_framecount + 1) & ( (1 << PS_PMOVEFRAMECOUNTBITS) - 1); + + // chop the move up if it is too long, to prevent framerate + // dependent behavior + while ( pmove->ps.commandTime != finalTime ) + { + int msec; + + msec = finalTime - pmove->ps.commandTime; + + if ( pmove->pmove_fixed ) + { + if ( msec > pmove->pmove_msec ) + msec = pmove->pmove_msec; + } + else + { + if ( msec > 66 ) + msec = 66; + } + + pmove->cmd.serverTime = pmove->ps.commandTime + msec; + + if (pmove->isInterior) + { + PmoveSingle( pmove ); + } + else + { + PmoveSingle( pmove ); + /* + std::map::const_iterator it = ExtCellLookup.find(PositionToCell(pmove->ps.origin) ); + if (it != ExtCellLookup.end() ) + { + pmove->traceObj->incellptr = it->second; + }*/ + } + + //if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) + //pmove->cmd.upmove = 20; + } + + //pmove->ps.last_compute_time = GetTimeQPC(); + //pmove->ps.lerp_multiplier = (pmove->ps.origin - pmove->ps.lastframe_origin);// * (1.000 / 31.0); + + //PM_CheckStuck(); + +} + + diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h new file mode 100644 index 0000000000..e46eb9d2e7 --- /dev/null +++ b/libs/openengine/bullet/pmove.h @@ -0,0 +1,200 @@ +#ifndef OENGINE_BULLET_PMOVE_H +#define OENGINE_BULLET_PMOVE_H +/* +This source file is a *modified* version of various header files from the Quake 3 Arena source code, +which was released under the GNU GPL (v2) in 2005. +Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. +*/ + +#include +#include +#include +#include "trace.h" +#include "physic.hpp" + + +//#include "GameMath.h" +//#include "GameTime.h" + +// Forwards-declare it! + +/*#ifndef COMPILING_PMOVE +#include "Scene.h" +extern SceneInstance* global_lastscene; +#endif*/ + +static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); + +#define MAX_CLIP_PLANES 5 +#define OVERCLIP 1.001f +//#define STEPSIZE 18 // 18 is way too much +#define STEPSIZE (18 / 2) +#ifndef M_PI + #define M_PI 3.14159265358979323846f +#endif +#define YAW 0 +#define PITCH /*1*/2 +#define ROLL /*2*/1 +#define SHORT2ANGLE(x) ( (x) * (360.0f / 65536.0f) ) +#define ANGLE2SHORT(x) ( (const short)( (x) / (360.0f / 65536.0f) ) ) +#define GENTITYNUM_BITS 10 // don't need to send any more +#define MAX_GENTITIES (1 << GENTITYNUM_BITS) +#define ENTITYNUM_NONE (MAX_GENTITIES - 1) +#define ENTITYNUM_WORLD (MAX_GENTITIES - 2) +#define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes +#define JUMP_VELOCITY (270) +#define PS_PMOVEFRAMECOUNTBITS 6 +#define MINS_Z -24 +#define DEFAULT_VIEWHEIGHT 26 +#define CROUCH_VIEWHEIGHT 12 +#define DEAD_VIEWHEIGHT (-16) +#define CONTENTS_SOLID 1 // an eye is never valid in a solid +#define CONTENTS_LAVA 8 +#define CONTENTS_SLIME 16 +#define CONTENTS_WATER 32 +#define CONTENTS_FOG 64 +static const float pm_accelerate = 10.0f; +static const float pm_stopspeed = 100.0f; +static const float pm_friction = 12.0f; +static const float pm_flightfriction = 3.0f; +static const float pm_waterfriction = 1.0f; +static const float pm_airaccelerate = 1.0f; +static const float pm_swimScale = 0.50f; +static const float pm_duckScale = 0.25f; +static const float pm_flyaccelerate = 8.0f; +static const float pm_wateraccelerate = 4.0f; + +enum pmtype_t +{ + PM_NORMAL, // can accelerate and turn + PM_NOCLIP, // noclip movement + PM_SPECTATOR, // still run into walls + PM_DEAD, // no acceleration or turning, but free falling + PM_FREEZE, // stuck in place with no control + PM_INTERMISSION, // no movement or status bar + PM_SPINTERMISSION // no movement or status bar +}; + +enum waterlevel_t +{ + WL_DRYLAND = 0, + WL_ANKLE, + WL_WAIST, + WL_UNDERWATER +}; + + +//#include "bprintf.h" + +struct playerMove +{ + struct playerStruct + { + playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + { + origin = Ogre::Vector3(733.164f,900.0f, 839.432f); + velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); + + viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f); + + delta_angles[0] = delta_angles[1] = delta_angles[2] = 0; + + lastframe_origin.x = lastframe_origin.y = lastframe_origin.z = 0; + lerp_multiplier.x = lerp_multiplier.y = lerp_multiplier.z = 0; + } + + inline void SpeedUp(void) + { + //printf("speed up to: %f\n", speed); + speed *= 1.25f; + } + + inline void SpeedDown(void) + { + //printf("speed down to %f\n", speed); + speed /= 1.25f; + } + + Ogre::Vector3 velocity; + Ogre::Vector3 origin; + bool bSnap; + bool snappingImplemented; + int counter; + float gravity; // default = 800 + float speed; // default = 320 + + int commandTime; // the time at which this command was issued (in milliseconds) + + int pm_time; + + Ogre::Vector3 viewangles; + + int groundEntityNum; + + int pmove_framecount; + + int watertype; + waterlevel_t waterlevel; + + signed short delta_angles[3]; + + pmtype_t move_type; + + float last_compute_time; + Ogre::Vector3 lastframe_origin; + Ogre::Vector3 lerp_multiplier; + } ps; + + struct playercmd + { + enum CMDstateChange + { + NO_CHANGE, + KEYDOWN, + KEYUP + }; + + playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false), + activating(false), lastActivatingState(false), procActivating(NO_CHANGE), + dropping(false), lastDroppingState(false), procDropping(NO_CHANGE) + { + angles[0] = angles[1] = angles[2] = 0; + } + + int serverTime; + + short angles[3]; + + signed char forwardmove; + signed char rightmove; + signed char upmove; + + bool ducking; + bool activating; // if the user is holding down the activate button + bool dropping; // if the user is dropping an item + + bool lastActivatingState; + bool lastDroppingState; + + CMDstateChange procActivating; + CMDstateChange procDropping; + } cmd; + + playerMove() : msec(50), pmove_fixed(false), pmove_msec(50), waterHeight(0), isInterior(true), hasWater(false) + { + } + + int msec; + int pmove_msec; + bool pmove_fixed; + int waterHeight; + bool hasWater; + bool isInterior; + //Object* traceObj; + OEngine::Physic::PhysicEngine* mEngine; +}; + +void Pmove (playerMove* const pmove); +void Ext_UpdateViewAngles(playerMove* const pm); +void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) ; +#endif diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp new file mode 100644 index 0000000000..2d18aaa4da --- /dev/null +++ b/libs/openengine/bullet/trace.cpp @@ -0,0 +1,190 @@ + +#include "trace.h" + + + +#include + + + + + + + +void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) //Traceobj was a Aedra Object +{ + + //if (!traceobj) + // return; + + //if (!traceobj->incellptr) + // return; + + const Ogre::Vector3 rayDir = end - start; + + // Nudge starting point backwards + //const Position3D nudgestart = start + (rayDir * -0.1f); // by 10% (isn't that too much?) + //const Position3D nudgestart = start; + + NewPhysTraceResults out; + //std::cout << "Starting trace\n"; + //Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45); + //Ogre::Vector3 endReplace = startReplace; + //endReplace.z -= .25; + + const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0, rotation), isInterior, enginePass); + + if (out.fraction < 0.001f) + results->startsolid = true; + else + results->startsolid = false; + + + //results->allsolid = out.startSolid; + + // If outside and underground, we're solid + /*if (isInterior) + { + const Ogre::Vector3 height = GetGroundPosition(start, CellCoords(traceCell->data->gridX, traceCell->data->gridY) ); + if (start.yPos - height.yPos < (-2.0f * BBHalfExtents.yPos) ) + { + results->allsolid = true; + } + else + results->allsolid = false; + }*/ + + // If inside and out of the tree, we're solid + //else + //{ + results->allsolid = out.startSolid; + //std::cout << "allsolid" << results->allsolid << "\n"; + //} + + if (!hasHit) + { + results->endpos = end; + results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); + results->entityNum = ENTITYNUM_NONE; + results->fraction = 1.0f; + } + else + { + results->fraction = out.fraction; + results->planenormal = out.hitNormal; + results->endpos = rayDir * results->fraction + start; + results->entityNum = ENTITYNUM_WORLD; + /*bprintf("Start: (%f, %f, %f) End: (%f, %f, %f) TraceDir: (%f, %f, %f) HitNormal: (%f, %f, %f) Fraction: %f Hitpos: (%f, %f, %f) CompensatedHitpos: (%f, %f, %f)\n", + start.xPos, start.yPos, start.zPos, + end.xPos, end.yPos, end.zPos, + rayDir.xPos, rayDir.yPos, rayDir.zPos, + results->planenormal.xPos, results->planenormal.yPos, results->planenormal.zPos, results->fraction, + out.endPos.xPos, out.endPos.yPos, out.endPos.zPos, + results->endpos.xPos, results->endpos.yPos, results->endpos.zPos);*/ + } +} + + + +template +const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, + const Ogre::Vector3& BBHalfExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) +{ + //if (!traceobj->incellptr) + // return false; + //if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex()) + // std::cout << "It's convex\n"; + + + + const btVector3 btstart(start.x, start.y, start.z); + const btVector3 btend(end.x, end.y, end.z); + const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z + + const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); + const btTransform from(btrot, btstart); + const btTransform to(btrot, btend); + + // warning: unused variable ... + /* + float x = from.getOrigin().getX(); + float y = from.getOrigin().getY(); + float z = from.getOrigin().getZ(); + float x2 = to.getOrigin().getX(); + float y2 = to.getOrigin().getY(); + float z2 = to.getOrigin().getZ(); + */ + + //std::cout << "BtFrom: " << x << "," << y << "," << z << "\n"; + //std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n"; + //std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n"; + + + btCollisionWorld::ClosestConvexResultCallback + newTraceCallback(btstart, btend); + + newTraceCallback.m_collisionFilterMask = (traceType == collisionWorldTrace) ? Only_Collision : Only_Pickup; + + + enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); + //newTraceCallback. + + + //std::cout << "NUM: " << enginePass->dynamicsWorld->getNumCollisionObjects() << "\n"; + + // Copy the hit data over to our trace results struct: + out->fraction = newTraceCallback.m_closestHitFraction; + + Ogre::Vector3& outhitnormal = out->hitNormal; + const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; + + outhitnormal.x = tracehitnormal.x(); + outhitnormal.y = tracehitnormal.y(); + outhitnormal.z = tracehitnormal.z(); + + Ogre::Vector3& outhitpos = out->endPos; + const btVector3& tracehitpos = newTraceCallback.m_hitPointWorld; + + outhitpos.x = tracehitpos.x(); + outhitpos.y = tracehitpos.y(); + outhitpos.z= tracehitpos.z(); + + // StartSolid test: + { + out->startSolid = false; + //btCollisionObject collision; + //collision.setCollisionShape(const_cast(&newshape) ); + + //CustomContactCallback crb; + + //world.world->contactTest(&collision, crb); + //out->startSolid = crb.hit; + + // If outside and underground, we're solid + if (!isInterior) //Check if we are interior + { + } + + // If inside and out of the tree, we're solid + else + { + btVector3 aabbMin, aabbMax; + enginePass->broadphase->getBroadphaseAabb(aabbMin, aabbMax); + //std::cout << "AABBMIN" << aabbMin.getX() <<"," <startSolid = true; + } + } + } + + const bool hasHit = newTraceCallback.hasHit(); + + + + + return hasHit; +} diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index 025673ab7a..d446b6854c 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -18,7 +18,7 @@ enum traceWorldType bothWorldTrace = collisionWorldTrace | pickWorldTrace }; -enum collaborativePhysicsType : unsigned +enum collaborativePhysicsType { No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass) Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics) @@ -53,10 +53,10 @@ struct traceResults template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); +//template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); +//template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -#endif \ No newline at end of file +#endif