00001 //----------------------------------------------------------------------------------- 00002 // 00003 // Pro-Vocation Light Engine (PVLE) 00004 // Copyright (C) 2007-2009 Sukender, KinoX & Buzib 00005 // For more information, contact us : sukender@free.fr 00006 // 00007 // This program is free software; you can redistribute it and/or modify 00008 // it under the terms of the GNU General Public License as published by 00009 // the Free Software Foundation; either version 3 of the License, or 00010 // (at your option) any later version. 00011 // 00012 // For any use that is not compatible with the terms of the GNU 00013 // General Public License, please contact the authors for alternative 00014 // licensing options. 00015 // 00016 // This program is distributed in the hope that it will be useful, 00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 // GNU General Public License for more details. 00020 // 00021 // You should have received a copy of the GNU General Public License along 00022 // with this program; if not, write to the Free Software Foundation, Inc., 00023 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00024 // 00025 //----------------------------------------------------------------------------------- 00026 00027 #include <PVLE/Config.h> 00028 #include <PVLE/Entity/Ammo.h> 00029 #include <PVLE/Physics/Contact.h> 00030 #include <PVLE/Physics/Body.h> 00031 #include <PVLE/Physics/World.h> 00032 #include <PVLE/Game/PVLEGame.h> 00033 #include <PVLE/Game/Simulation.h> 00034 #include <algorithm> 00035 00036 00039 class Dummy3DPhy : public C3DPhy { 00040 public: 00042 Dummy3DPhy(C3DPhy * pV) : C3DPhy(*pV, COPY_PHYSICS), pInit3DPhy(pV) { ASSERT(pV); } 00043 virtual void step(dReal) { onTTLZero(); } 00044 00046 virtual bool hitBefore(Physics::Contact * pContacts, unsigned int nbContacts, Physics::Contact * pMaxEnergyContact, Physics::Geom & thisGeom, Physics::Geom & otherGeom, Physics::Body *& inout_pThisBody, Physics::Body *& inout_pOtherBody) { 00047 IGeomCollisionContainer * pContainer = otherGeom.getCollisionContainer(); 00048 if (!pContainer) return true; 00049 return (pInit3DPhy != pContainer->as3DPhy()); 00050 } 00051 00052 protected: 00053 const C3DPhy * pInit3DPhy; 00054 }; 00055 00056 // ------------------------------------------------------------------------------------ 00057 00058 00059 const float perforationToCinetik = 0.1f; // Should be low (or the ammo will "perfore and push" the geom, so that perforation will last "forever") 00060 00061 00062 Ammo::Ammo(osg::MatrixTransform * pModel, Physics::Geom * pGeom, C3DPhy::EInitOptions options, const C3DPhy * pInheritTeamAndPlayer) : 00063 C3DPhy(pModel, pGeom, options, pInheritTeamAndPlayer), canBeDeletedOnNextFrame(false), minSpeed2(-1), minCollisionCineticToDestroy(0), lastPerforationEnergy(0) 00064 { 00065 std::fill(vParams, vParams+WeaponEnergy::MAX_WEAPON_ENERGY, 0.f); 00066 } 00067 00068 Ammo::Ammo(const C3DPhy & v, UINT copyOpts) : C3DPhy(v, copyOpts), canBeDeletedOnNextFrame(false), minSpeed2(-1), minCollisionCineticToDestroy(0), lastPerforationEnergy(0) 00069 { 00070 std::fill(vParams, vParams+WeaponEnergy::MAX_WEAPON_ENERGY, 0.f); 00071 } 00072 00073 Ammo::Ammo(const Ammo & v, UINT copyOpts) : C3DPhy(v, copyOpts), canBeDeletedOnNextFrame(false), minSpeed2(v.minSpeed2), minCollisionCineticToDestroy(v.minCollisionCineticToDestroy), lastPerforationEnergy(0) 00074 { 00075 memcpy(vParams, v.vParams, sizeof(float) * WeaponEnergy::MAX_WEAPON_ENERGY); // Could use std::copy()... 00076 } 00077 00078 //Ammo::Ammo() : 00079 // C3DPhy(), canBeDeletedOnNextFrame(false), minSpeed2(-1), minCollisionCineticToDestroy(0) 00080 //{ 00081 // std::fill(vParams, vParams+WeaponEnergy::MAX_WEAPON_ENERGY, 0.f); 00082 //} 00083 00084 00085 void Ammo::step(dReal stepSize) { 00086 ASSERT(vGeoms.size()>0 && vGeoms[0]->getBody()); // Ammo must have a body 00087 00088 C3DPhy::step(stepSize); // Updates TTL 00089 // Destroys ammo if not enough absolute speed (relative speed to each other geom would need to much computation !) 00090 if (ttl>=0 && minSpeed2>0 && dBodyGetLinearVelV(*(vGeoms[0]->getBody())).length2() < minSpeed2) onTTLZero(); 00091 } 00092 00093 00094 bool Ammo::hitBefore(Physics::Contact * pContacts, unsigned int nbContacts, Physics::Contact * pMaxEnergyContact, Physics::Geom & thisGeom, Physics::Geom & otherGeom, Physics::Body *& inout_pThisBody, Physics::Body *& inout_pOtherBody) { 00095 // Technique for this method is to let ammo go thru the geom, and compute perforation on each step. This will do as if the length of the perforation is computed. 00096 // As ammo go thru, it's nonsense to create a contact joint between ammo and geom. For this reason, a dummy 3DPhy that represents the ammo si created on collision. A contact joint will be created with that dummy object, so that geom will take cinecic energy. 00097 00098 lastPerforationEnergy = 0; // Reset 00099 00100 // Life points removal 00101 C3DPhy * pOther3DPhy = NULL; 00102 IGeomCollisionContainer * pContainer = otherGeom.getCollisionContainer(); 00103 if (pContainer) pOther3DPhy = pContainer->as3DPhy(); 00104 bool collision = true; // Can be set to "false" if ammo goes thru and no dummy is created. 00105 00106 if (pOther3DPhy) { // Do not perform life points removal if hit object is not a 3D-Phy 00107 ASSERT(vGeoms.size()>0 && vGeoms[0]->getBody()); // Ammo must have a body 00108 Physics::Body * pBody = vGeoms[0]->getBody(); 00109 00110 // Assert ammunition is not perforant AND radiant (impossible, or let's say disabled because difficult to compute a perforation/reflexion/refraction/absorption) 00111 ASSERT(vParams[WeaponEnergy::PERFORANT] <=0 || vParams[WeaponEnergy::RADIANT] <=0); 00112 00113 // Compute perforation 00114 if (vParams[WeaponEnergy::PERFORANT] >0) { 00115 // perforationEnergy must not be dependant of the step size : if step is short, perforation will occur more times for the same situation, so it's multiplied by the step size. 00116 float perforationEnergy = vParams[WeaponEnergy::PERFORANT] * otherGeom.getGameplayDensity() * SimulationHolder::instance().get()->getCurPhyStep(); 00117 ASSERT(perforationEnergy>=0); 00118 00119 // Remove perforation energy to cinetic energy (until 0 ! => adjust perforation energy if necessary), by adjusting velocity 00120 osg::Vec3 vel = dBodyGetLinearVelV(*pBody); 00121 osg::Vec3 velNormalized(vel); velNormalized.normalize(); 00122 00123 float mass = pBody->getMassData().getMass(); 00124 float currentCineticEnergy = vel.length2() * mass * .5f; 00125 float newCineticEnergy = currentCineticEnergy - perforationEnergy; 00126 if (newCineticEnergy <= 0) { 00127 canBeDeletedOnNextFrame = true; 00128 perforationEnergy = currentCineticEnergy; // Adjust perforation energy 00129 newCineticEnergy = 0; 00130 dBodySetLinearVel(*pBody, 0,0,0); 00131 } else { 00132 // Set new velocity (normalize, and multiply according to cinetic) 00133 dBodySetLinearVel(*pBody, velNormalized * sqrtf(newCineticEnergy / mass * 2.f)); 00134 } 00135 LOG_ALWAYS << "Perforation : Ammo new speed = " << sqrtf(newCineticEnergy / mass * 2.f) << "(" << vel.length() << ")" << std::endl; 00136 00137 // Split perforation energy into two : pure perforation damage, and cinetic energy that will be transferred to otherGeom 00139 00140 // Transfer "pure perforation" energy to the other geom 00141 LOG_ALWAYS << " Perforant damage (without firendly fire factor) = " << perforationEnergy * (1-perforationToCinetik) << std::endl; 00142 // Don't call isHitByPerforant() here, but in hitAfter. 00143 //pOther3DPhy->isHitByPerforant(perforationEnergy * (1-perforationToCinetik) * friendlyFireCoef, pMaxEnergyContact->getPos()); 00144 lastPerforationEnergy = perforationEnergy * (1-perforationToCinetik); // Will be used by hitAfter(), which is guaranteed to be called after this method. 00145 00146 // Transfer cinetic energy of perforation (only if other has a body !). 00147 // Do not create a contact joint directly, but another 3D-Phy that will do the job "as if". 00148 if (inout_pOtherBody) { 00149 // Create a 3D-Phy that will destroy itself after 1 step 00150 // Dummy3DPhy ensures that no collision will happen between original & dummy 00151 Dummy3DPhy * pDummyAmmo = new Dummy3DPhy(this); 00152 p3DPhyOwner->addInScene(pDummyAmmo); // BROKEN FEATURE : connot add a geom during a world step 00153 // Set correct body for collision 00154 inout_pThisBody = pDummyAmmo->getGeom(0)->getBody(); 00155 // Set speed of the dummy 00156 // Dev Note : if ammo has more than one body, this will be a problem... 00157 dBodySetLinearVel(*inout_pThisBody, velNormalized * sqrtf((perforationEnergy * perforationToCinetik) / mass * 2.f)); 00158 LOG_ALWAYS << " Dummy ammo speed = " << sqrtf((perforationEnergy * perforationToCinetik) / mass * 2.f) << std::endl; 00159 } else collision = false; // Disable collision if no dummy exists (no collision between original ammo and other geom) 00160 } 00161 } 00162 00163 // Delete ammunition on next frame, only if a contact joint is created (set only a flag) 00164 // Ammo is also checked against collision angle : it should not be destroyed if "just touching". Collision energy depends on the angle : if pMaxEnergyContact->arithmeticCineticEnergy is low, then ammo should bounce intead of being destroyed. 00166 if (pMaxEnergyContact->arithmeticCineticEnergy > minCollisionCineticToDestroy) canBeDeletedOnNextFrame = true; 00167 00168 // Create a contact, even if destroyed (transfer cinetic energy) 00169 return collision; 00170 } 00171 00172 00173 void Ammo::hitAfter(Physics::Contact * pContacts, unsigned int nbContacts, Physics::Contact * pMaxEnergyContact, Physics::Geom & thisGeom, Physics::Geom & otherGeom, bool contactJointCreated) { 00174 if (contactJointCreated) { 00175 // -- Energy transfer for 3DPhys -- 00176 // For perforation energy, we need the data from the perforation computed in hitBefore(). 00177 // For all other energies, we simply transfer (for maximized cinetic energy contact). 00178 IGeomCollisionContainer * pContainer = otherGeom.getCollisionContainer(); 00179 if (pContainer) { 00180 C3DPhy * pOther3DPhy = pContainer->as3DPhy(); 00181 if (pOther3DPhy) { 00182 // Test for friendly fire 00183 float friendlyFireCoef; 00184 if (pTeam && pTeam == pOther3DPhy->getTeam()) { 00185 // Transfer energies with a coefficient, which is a Game parameter. 00186 // Note that collision cinetic energy is not multiplied by a coef (Or it would be done on every collision); but "fake" (written in vParams[]) cinetic energy is. 00187 friendlyFireCoef = PVLEGameHolder::instance().get()->getFriendlyFire(); 00188 } else friendlyFireCoef = 1; 00189 00190 for(int it=0; it!=WeaponEnergy::MAX_WEAPON_ENERGY; ++it) { 00191 pOther3DPhy->isHitBy(static_cast<WeaponEnergy::EEneryType>(it), 00192 (it == WeaponEnergy::PERFORANT ? lastPerforationEnergy : vParams[it]) * friendlyFireCoef, 00193 pMaxEnergyContact->getPos()); 00194 } 00195 } 00196 } 00197 } 00198 00199 if (canBeDeletedOnNextFrame) { 00200 if (contactJointCreated) onTTLZero(); // Never modify geom or model after this call (See C3DPhyOwner documentation) 00201 else canBeDeletedOnNextFrame = false; 00202 } 00203 } 00204 00205 00206 #ifdef PVLE_NETWORKING 00207 //TNL::U32 Ammo::packUpdate(TNL::GhostConnection * pConnection, TNL::U32 mask, TNL::BitStream * pStream) { 00208 // if (pStream->writeFlag((mask & INITIAL_MASK)!=0)) { 00209 // BOOST_FOREACH(float f, vParams) pStream->write(f); 00210 // pStream->write(minSpeed2); 00211 // pStream->write(minCollisionCineticToDestroy); 00212 // } 00213 // 00214 // C3DPhy::packUpdate(pConnection, mask, pStream); 00215 // return 0; 00216 //} 00217 // 00218 //void Ammo::unpackUpdate(TNL::GhostConnection * pConnection, TNL::BitStream * pStream) { 00219 // // INITIAL_MASK 00220 // if (pStream->readFlag()) { 00221 // BOOST_FOREACH(float & f, vParams) pStream->read(&f); 00222 // pStream->read(&minSpeed2); 00223 // pStream->read(&minCollisionCineticToDestroy); 00224 // 00225 // //Game * pGame = PVLEGameHolder::instance().get<Game>(); 00226 // PVLEGame * pGame = PVLEGameHolder::instance().get(); 00227 // //pPhyWorld = pGame->getPhyWorld(); 00228 // //createGeom(pPhyWorld, osg::Vec3(0,0,0)); 00229 // //bind(); 00230 // pGame->addInScene(this); 00231 // } 00232 // 00233 // C3DPhy::unpackUpdate(pConnection, pStream); 00234 //} 00235 #endif 00236 00237 00238 // -------------------------------------------------------------------------------------------------------- 00239 // 00240 //#include "PVLE/Physics/Geom.h" 00241 //#include "PVLE/Physics/Body.h" 00242 //#include "PVLE/Physics/CommonSurfaceParams.h" 00243 //#include "PVLE/Physics/Utility.h" 00244 // 00245 //#include <PVLE/3D/Commons.h> 00246 // 00247 // 00248 //C3DPhy * createBullet(Physics::World * pPhyWorld, const osg::Matrix & matrixWTL, const osg::Vec3 & velocity, double ttl, const C3DPhy * pInheritTeamAndPlayer) { 00249 // const double radius = 1; 00250 // //const float mass = 0.009f; // 7.62 ammo mass 00251 // const float mass = 0.5f; 00252 // Physics::Geom * pGeom = Physics::createCanonicalSphere(pPhyWorld, radius, mass, Physics::Materials::METAL); 00253 // Physics::Body * pBody = pGeom->getBody(); 00254 // pBody->setMatrix(matrixWTL); // Set position and rotation 00255 // pBody->setLinearVel(velocity); 00256 // 00257 // // 3D representation of the object 00258 // osg::MatrixTransform * pPhyDrivenMat = createShapeGeodeSphere(radius); 00259 // setColor(pPhyDrivenMat, osg::Vec4f(1,1,0,1)); 00260 // 00261 // float velLen2 = velocity.length2(); 00262 // Ammo * pRes = new Ammo(pPhyDrivenMat, pGeom, C3DPhy::INIT_AUTO_BIND, pInheritTeamAndPlayer); 00263 // pRes->setTTL(ttl); 00264 // pRes->setMinSpeed2(velLen2/10); // Destroy ammo if velocity is 1/10th of the initial velocity 00265 // //pRes->setMinCollisionCineticToDestroy(velLen2*mass*.5f); // Destroy ammo if it hits a geom with this ammount of energy (= equivalent to cinetic energy against a body of 1 kg) 00266 // //pRes->setMinCollisionCineticToDestroy(1e20); // Never destroy ammo on collision 00267 // pRes->setMinCollisionCineticToDestroy(1e3); 00268 // pRes->setParam(WeaponEnergy::THERMAL, 2000); 00269 // //pRes->setParam(WeaponEnergy::PERFORANT, 30); 00270 // return pRes; 00271 //}