Files
scummvm-cursorfix/engines/hpl1/engine/physics/PhysicsBody.cpp
2026-02-02 04:50:13 +01:00

565 lines
16 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
/*
* 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<iPhysicsJoint *>::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<iCharacterBody *>::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<cSaveData_iCollideShape> *apShapeList,
iPhysicsWorld *apWorld) {
cContainerListIterator<cSaveData_iCollideShape> 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<cSaveData_iCollideShape> *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