/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copyright (C) 2006-2010 - Frictional Games * * This file is part of HPL1 Engine. */ #include "hpl1/engine/physics/PhysicsBody.h" #include "hpl1/engine/physics/CollideShape.h" #include "hpl1/engine/physics/PhysicsJoint.h" #include "hpl1/engine/physics/PhysicsMaterial.h" #include "hpl1/engine/physics/PhysicsWorld.h" #include "hpl1/engine/physics/SurfaceData.h" #include "hpl1/engine/system/low_level_system.h" #include "hpl1/engine/game/Game.h" #include "hpl1/engine/scene/Scene.h" #include "hpl1/engine/scene/World3D.h" #include "hpl1/engine/scene/SoundEntity.h" #include "hpl1/engine/sound/SoundChannel.h" #include "hpl1/engine/sound/SoundHandler.h" #include "hpl1/engine/scene/Node3D.h" #include "common/algorithm.h" #include "hpl1/engine/math/Math.h" namespace hpl { ////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- iPhysicsBody::iPhysicsBody(const tString &asName, iPhysicsWorld *apWorld, iCollideShape *apShape) : iEntity3D(asName) { mpWorld = apWorld; mpShape = apShape; mpNode = NULL; // Increment user count for the shape apShape->IncUserCount(); mBoundingVolume.SetLocalMinMax(apShape->GetBoundingVolume().GetMin(), apShape->GetBoundingVolume().GetMax()); // Set the default material so that body always has a material. mpMaterial = mpWorld->GetMaterialFromName("Default"); mpCharacterBody = NULL; mbBlocksSound = false; mbBlocksLight = true; mpScrapeBody = NULL; mpScrapeSoundEntity = NULL; mpRollSoundEntity = NULL; mbHasImpact = false; mbHasSlide = false; mlSlideCount = 0; mlImpactCount = 0; mlBuoyancyId = -1; mbCanAttachCharacter = false; mpUserData = NULL; mbPushedByCharacterGravity = false; mbIsCharacter = false; mbCollideCharacter = true; mbIsRagDoll = false; mbCollideRagDoll = true; mbCollide = true; mbVolatile = false; mbDisableAfterSimulation = false; mbHasCollision = false; m_mtxPrevScrapeMatrix = cMatrixf::Identity; // Log("Creating body %s\n",msName.c_str()); } //----------------------------------------------------------------------- iPhysicsBody::~iPhysicsBody() { } //----------------------------------------------------------------------- void iPhysicsBody::Destroy() { // Log("Start Destroying newton body '%s' %d\n",msName.c_str(),(size_t)this); if (mpNode) hplDelete(mpNode); mpWorld->DestroyShape(mpShape); // Log(" Joints\n"); for (int i = 0; i < (int)mvJoints.size(); i++) { iPhysicsJoint *pJoint = mvJoints[i]; pJoint->RemoveBody(this); if (pJoint->GetParentBody() == NULL && pJoint->GetChildBody() == NULL) { mpWorld->DestroyJoint(pJoint); } // Skip removing for now, just makes things messy... /*if( pJoint->GetParentBody() == this || (pJoint->GetParentBody() == NULL && pJoint->GetChildBody()== this) ) { //Log(" Destroy joint %d\n",(size_t)pJoint); mpWorld->DestroyJoint(pJoint); } else if(pJoint->GetParentBody() == this) { //Remove this body from the joint pJoint->RemoveBody(this); }*/ } // Log("Deleted body '%s'\n",msName.c_str()); if (mpScrapeSoundEntity && mpWorld->GetWorld3D()->SoundEntityExists(mpScrapeSoundEntity)) mpWorld->GetWorld3D()->DestroySoundEntity(mpScrapeSoundEntity); if (mpRollSoundEntity && mpWorld->GetWorld3D()->SoundEntityExists(mpRollSoundEntity)) mpWorld->GetWorld3D()->DestroySoundEntity(mpRollSoundEntity); DeleteLowLevel(); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cVector3f iPhysicsBody::GetVelocityAtPosition(cVector3f avPos) { return GetLinearVelocity() + cMath::Vector3Cross(GetAngularVelocity(), avPos - GetLocalPosition()); } //----------------------------------------------------------------------- void iPhysicsBody::AddJoint(iPhysicsJoint *apJoint) { mvJoints.push_back(apJoint); } iPhysicsJoint *iPhysicsBody::GetJoint(int alIndex) { return mvJoints[alIndex]; } int iPhysicsBody::GetJointNum() { return (int)mvJoints.size(); } void iPhysicsBody::RemoveJoint(iPhysicsJoint *apJoint) { Common::Array::iterator it = mvJoints.begin(); for (; it != mvJoints.end(); ++it) { if (*it == apJoint) { mvJoints.erase(it); break; } } } //----------------------------------------------------------------------- void iPhysicsBody::UpdateBeforeSimulate(float afTimeStep) { // if(msName == "headalive01_throat2") Log("headalive01_throat2 Active: %d\n", IsActive()); // if(msName == "headalive01_throat") Log("headalive01_throat Active: %d\n", IsActive()); // Reset that the body has had contact SetHasSlide(false); SetHasImpact(false); mbHasCollision = false; } void iPhysicsBody::UpdateAfterSimulate(float afTimeStep) { ////////////////////////////////// // Check disabling from callback if (mbDisableAfterSimulation) { mbDisableAfterSimulation = false; SetEnabled(false); } ////////////////////////////////// // Check slide sound if (HasSlide() == false) { if (GetScrapeSoundEntity()) { if (mlSlideCount <= 0) { // Log("Stopped scrape %d on body '%s' IN BODY!\n", (size_t)GetScrapeSoundEntity(), // GetName().c_str()); if (mpWorld->GetWorld3D()) if (mpWorld->GetWorld3D()->SoundEntityExists(GetScrapeSoundEntity())) { GetScrapeSoundEntity()->FadeOut(5.2f); } SetScrapeSoundEntity(NULL); SetScrapeBody(NULL); } else if (mlSlideCount > 0) { mlSlideCount--; } } } else { mlSlideCount = 8; } ////////////////////////////////// // Update rolling sound if (mpMaterial) mpMaterial->GetSurfaceData()->UpdateRollEffect(this); } //----------------------------------------------------------------------- cNode3D *iPhysicsBody::GetNode() { return mpNode; } cNode3D *iPhysicsBody::CreateNode() { if (mpNode) return mpNode; mpNode = hplNew(cNode3D, ()); return mpNode; } //----------------------------------------------------------------------- void iPhysicsBody::AddBodyCallback(iPhysicsBodyCallback *apCallback) { mlstBodyCallbacks.push_back(apCallback); } void iPhysicsBody::RemoveBodyCallback(iPhysicsBodyCallback *apCallback) { tPhysicsBodyCallbackListIt it = mlstBodyCallbacks.begin(); for (; it != mlstBodyCallbacks.end(); ++it) { if (apCallback == *it) { mlstBodyCallbacks.erase(it); break; } } } //----------------------------------------------------------------------- bool iPhysicsBody::OnBeginCollision(iPhysicsBody *apBody) { if (mlstBodyCallbacks.empty()) return true; bool bReturn = true; // Log("Checking before collide callbacks for '%s' ",apBody->GetName().c_str()); tPhysicsBodyCallbackListIt it = mlstBodyCallbacks.begin(); for (; it != mlstBodyCallbacks.end(); ++it) { iPhysicsBodyCallback *pCallback = *it; // Log("Callback %d ,",(size_t)pCallback); if (pCallback->OnBeginCollision(this, apBody) == false) bReturn = false; } // Log(" END\n"); return bReturn; } //----------------------------------------------------------------------- void iPhysicsBody::OnCollide(iPhysicsBody *apBody, cPhysicsContactData *apContactData) { mbHasCollision = true; if (mlstBodyCallbacks.empty()) return; // Log("Checking on collide callbacks for '%s' ",apBody->GetName().c_str()); tPhysicsBodyCallbackListIt it = mlstBodyCallbacks.begin(); for (; it != mlstBodyCallbacks.end(); ++it) { iPhysicsBodyCallback *pCallback = *it; // Log("Callback %d ,",(size_t)pCallback); pCallback->OnCollide(this, apBody, apContactData); } // Log(" END\n"); } //----------------------------------------------------------------------- iPhysicsMaterial *iPhysicsBody::GetMaterial() { return mpMaterial; } //----------------------------------------------------------------------- iCollideShape *iPhysicsBody::GetShape() { return mpShape; } //----------------------------------------------------------------------- void iPhysicsBody::AddAttachedCharacter(iCharacterBody *apChar) { RemoveAttachedCharacter(apChar); mlstAttachedCharacters.push_back(apChar); } void iPhysicsBody::RemoveAttachedCharacter(iCharacterBody *apChar) { Common::List::iterator it = mlstAttachedCharacters.begin(); for (; it != mlstAttachedCharacters.end(); ++it) { if (apChar == *it) { mlstAttachedCharacters.erase(it); break; } } } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // SAVE OBJECT STUFF ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- kBeginSerializeBase(cSaveData_iCollideShape) kSerializeVar(mType, eSerializeType_Int32) kSerializeVar(m_mtxOffset, eSerializeType_Matrixf) kSerializeVar(mvSize, eSerializeType_Vector3f) kEndSerialize() //----------------------------------------------------------------------- kBeginSerialize(cSaveData_iPhysicsBody, cSaveData_iEntity3D) kSerializeClassContainer(mlstShapes, cSaveData_iCollideShape, eSerializeType_Class) kSerializeVar(msMaterial, eSerializeType_String) kSerializeVar(mbBlocksSound, eSerializeType_Bool) kSerializeVar(mbIsCharacter, eSerializeType_Bool) kSerializeVar(mbCollideCharacter, eSerializeType_Bool) kSerializeVar(mvLinearVelocity, eSerializeType_Vector3f) kSerializeVar(mvAngularVelocity, eSerializeType_Vector3f) kSerializeVar(mfLinearDamping, eSerializeType_Float32) kSerializeVar(mfAngularDamping, eSerializeType_Float32) kSerializeVar(mfMaxLinearSpeed, eSerializeType_Float32) kSerializeVar(mfMaxAngularSpeed, eSerializeType_Float32) kSerializeVar(mfMass, eSerializeType_Float32) kSerializeVar(mbEnabled, eSerializeType_Bool) kSerializeVar(mbAutoDisable, eSerializeType_Bool) kSerializeVar(mbContinuousCollision, eSerializeType_Bool) kSerializeVar(mbGravity, eSerializeType_Bool) kSerializeVar(mbCollide, eSerializeType_Bool) kEndSerialize() //----------------------------------------------------------------------- static iCollideShape *_CreateShape(cSaveData_iCollideShape *apData, iPhysicsWorld *apWorld) { switch ((eCollideShapeType)apData->mType) { case eCollideShapeType_Box: return apWorld->CreateBoxShape(apData->mvSize, &apData->m_mtxOffset); case eCollideShapeType_Sphere: return apWorld->CreateSphereShape(apData->mvSize, &apData->m_mtxOffset); case eCollideShapeType_Cylinder: return apWorld->CreateCylinderShape(apData->mvSize.x, apData->mvSize.y, &apData->m_mtxOffset); case eCollideShapeType_Capsule: return apWorld->CreateCapsuleShape(apData->mvSize.x, apData->mvSize.y, &apData->m_mtxOffset); case eCollideShapeType_Null: case eCollideShapeType_ConvexHull: case eCollideShapeType_Mesh: case eCollideShapeType_Compound: case eCollideShapeType_LastEnum: break; } Warning("Invalid shape type %d!\n", apData->mType); return NULL; } static iCollideShape *CreateCollideShapeFromSave(cContainerList *apShapeList, iPhysicsWorld *apWorld) { cContainerListIterator it = apShapeList->GetIterator(); if (apShapeList->Size() == 1) { return _CreateShape(&it.Next(), apWorld); } else { tCollideShapeVec vShapes; while (it.HasNext()) { vShapes.push_back(_CreateShape(&it.Next(), apWorld)); } return apWorld->CreateCompundShape(vShapes); } } iSaveObject *cSaveData_iPhysicsBody::CreateSaveObject(cSaveObjectHandler *apSaveObjectHandler, cGame *apGame) { iPhysicsWorld *pWorld = apGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); // Get the collider iCollideShape *pShape = CreateCollideShapeFromSave(&mlstShapes, pWorld); if (pShape == NULL) return NULL; iPhysicsBody *pBody = pWorld->CreateBody(msName, pShape); return pBody; } //----------------------------------------------------------------------- int cSaveData_iPhysicsBody::GetSaveCreatePrio() { return 0; } //----------------------------------------------------------------------- iSaveData *iPhysicsBody::CreateSaveData() { return hplNew(cSaveData_iPhysicsBody, ()); } //----------------------------------------------------------------------- void iPhysicsBody::SaveToSaveData(iSaveData *apSaveData) { kSaveData_SaveToBegin(iPhysicsBody); // Collider CreateSaveCollideShapes(&pData->mlstShapes); // Material pData->msMaterial = mpMaterial == NULL ? "" : mpMaterial->GetName(); // Save vars kSaveData_SaveTo(mbBlocksSound); kSaveData_SaveTo(mbIsCharacter); kSaveData_SaveTo(mbCollideCharacter); // Save interface properties pData->mvLinearVelocity = GetLinearVelocity(); pData->mvAngularVelocity = GetAngularVelocity(); pData->mfLinearDamping = GetLinearDamping(); pData->mfAngularDamping = GetAngularDamping(); pData->mfMaxLinearSpeed = GetMaxLinearSpeed(); pData->mfMaxAngularSpeed = GetMaxAngularSpeed(); pData->mfMass = GetMass(); pData->mbEnabled = GetEnabled(); pData->mbAutoDisable = GetAutoDisable(); pData->mbContinuousCollision = GetContinuousCollision(); pData->mbGravity = GetGravity(); pData->mbCollide = GetCollide(); } //----------------------------------------------------------------------- void iPhysicsBody::LoadFromSaveData(iSaveData *apSaveData) { kSaveData_LoadFromBegin(iPhysicsBody); // Material if (pData->msMaterial != "") { iPhysicsMaterial *pMat = mpWorld->GetMaterialFromName(pData->msMaterial); if (pMat) SetMaterial(pMat); } // Save vars kSaveData_LoadFrom(mbBlocksSound); kSaveData_LoadFrom(mbIsCharacter); kSaveData_LoadFrom(mbCollideCharacter); // Save interface properties SetLinearVelocity(pData->mvLinearVelocity); SetAngularVelocity(pData->mvAngularVelocity); SetLinearDamping(pData->mfLinearDamping); SetAngularDamping(pData->mfAngularDamping); SetMaxLinearSpeed(pData->mfMaxLinearSpeed); SetMaxAngularSpeed(pData->mfMaxAngularSpeed); SetMass(pData->mfMass); SetEnabled(pData->mbEnabled); SetAutoDisable(pData->mbAutoDisable); SetContinuousCollision(pData->mbContinuousCollision); SetGravity(pData->mbGravity); SetCollide(pData->mbCollide); } //----------------------------------------------------------------------- void iPhysicsBody::SaveDataSetup(cSaveObjectHandler *apSaveObjectHandler, cGame *apGame) { kSaveData_SetupBegin(iPhysicsBody); } //----------------------------------------------------------------------- void iPhysicsBody::CreateSaveCollideShapes(cContainerList *apShapeList) { if (mpShape->GetType() == eCollideShapeType_Compound) { for (int i = 0; i < mpShape->GetSubShapeNum(); ++i) { iCollideShape *pShape = mpShape->GetSubShape(i); cSaveData_iCollideShape Shape; Shape.mType = (int)pShape->GetType(); Shape.m_mtxOffset = pShape->GetOffset(); Shape.mvSize = pShape->GetSize(); apShapeList->Add(Shape); } } else { cSaveData_iCollideShape Shape; Shape.mType = (int)mpShape->GetType(); Shape.m_mtxOffset = mpShape->GetOffset(); Shape.mvSize = mpShape->GetSize(); apShapeList->Add(Shape); } } //----------------------------------------------------------------------- } // namespace hpl