Files
2025-10-28 14:05:46 -04:00

416 lines
11 KiB
C++

/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */
#pragma once
#include "V8Kernel/KernelIndex.h"
#include "V8Kernel/Cofm.h"
#include "V8Kernel/SimBody.h"
#include "V8Kernel/Link.h"
#include "Util/IndexArray.h"
#include "Util/IndexedTree.h"
#include "Util/Memory.h"
#include "rbx/threadsafe.h"
class btCollisionObject;
namespace RBX {
/*
Body class. Handles rigid joints and kinematic/dynamic joints. Automatically calculates
mass properties. Automatically updates state.
StateRoot: Body or Joint that contains the latest state id.
RigidRoot: Body that I am clumped with
COFM: Calculated for every body in a rigid group - chain is broken for linked bodies
SIMBody: Only Free Bodies have this
Whenever adjusting a joint, must increment the stateRoot of the assembly
World Body // Special Body - no COFM
| |
| -- Body // anchored body - rigid join to the world body - when moving, adjust state index
|
-- Body // anchored body - rigid join to the world body
|
-- Body
|
-- Body
0 // this is a link
-- Body // this is the StateRoot for the chain below it
|
Body
Body (free) // this is the StateRoot for the free body and chain
| // this body has group COFM for the top three bodies + SimBody
-- Body
|
-- Body
0
-- Body // this body has a group COFM for the bodies below it
|
Body
COFM: body, kinematic, assembly (assumes a floating object)
*/
class BodyPvSetter;
class Body : public IndexedTree
, public Allocator<Body>
{
public:
friend class KernelData;
friend class Kernel;
friend class SimBody;
private:
rbx::spin_mutex mutex; // for safe calls that require update
// Unique identifier set by the World
boost::uint64_t uid;
int guidIndex;
int leafBodyIndex;
int& getLeafBodyIndex() {return leafBodyIndex;}
int connectorUseCount; // how many connectors connect this body
static Body* worldBody;
static void initStaticData(); // inits the world body
Body* root; // top body - either anchored or 6 dof
Cofm* cofm;
Cofm* getCofm() {return cofm;} // use these to insure const correctness;
SimBody* simBody; // Only present for parent && in kernel
SimBody* getSimBody() {return simBody;}
const SimBody* getConstSimBody() const {return simBody;}
void refreshCofm();
Link* link; // if link != NULL, use for "getMeInParent()"
// defining variables
bool canThrottle; // this body can throttle - i.e., slow down
CoordinateFrame meInParent; // used if no link, i.e. - rigid connection
Matrix3 moment;
float mass;
Vector3 cofmOffset;
// resulting variables
unsigned int stateIndex;
PV pv;
void resetRoot(Body* newRoot);
bool validateParentCofmDirty();
const CoordinateFrame& getMeInParent() {
RBXASSERT(getParent());
return getLink() ? getLink()->getChildInParent() : meInParent;
}
const CoordinateFrame& getConstMeInParent() const {
RBXASSERT(getConstParent());
RBXASSERT(!getConstLink()); // fails if linked (i.e. only works in same clump);
return meInParent;
}
void updatePV(); // Does not inline anyhow.
bool pvIsUpToDate() const {
if (!getConstParent()) {
return true;
}
else {
if (stateIndex != getRoot()->getStateIndexNoUpdate()) {
return false;
}
else {
return getConstParent()->pvIsUpToDate();
}
}
}
void onChildAdded(Body* newChild);
void onChildRemoved(Body* newChild);
Body* calcRoot() {return getParent() ? getParent()->calcRoot() : this;}
const Body* calcRootConst() const {return getConstParent() ? getConstParent()->calcRootConst() : this;}
///////////////////////////////////////////////
// Indexed Tree
/*override*/ void onParentChanging();
/*override*/ void onParentChanged(IndexedTree* oldParent);
/*override*/ void onChildAdding(IndexedTree* child);
/*override*/ void onChildAdded(IndexedTree* child);
/*override*/ void onChildRemoved(IndexedTree* child);
public:
Body();
~Body();
static unsigned int getNextStateIndex();
// Static world body
static Body* getWorldBody();
SimBody* getRootSimBody() {return getRoot()->getSimBody();}
const SimBody* getConstRootSimBody() const {return getRoot()->getConstSimBody();}
void setUID( boost::uint64_t _uid );
boost::uint64_t getUID() const { return uid; }
// Used only by the solver inspector to id the objects between different clients
void setGuidIndex( int _guidIndex ) { guidIndex = _guidIndex; }
int getGuidIndex() const { return guidIndex; }
//////////////////////////////////////////////////////
// From Cofm, SimBody
bool cofmIsClean() {return getCofm() ? !getCofm()->getIsDirty() : true;}
//////////////////////////////////////////////////////
// From Child
void makeCofmDirty();
void advanceStateIndex();
void makeStateDirty() {
getRoot()->advanceStateIndex();
}
unsigned int getStateIndex() {
updatePV();
return stateIndex;
}
unsigned int getStateIndexNoUpdate() const {
return stateIndex;
}
//////////////////////////////////////////////////////
// Base vs. Branch
Body* getChild(int i) {return getTypedChild<Body>(i);}
const Body* getConstChild(int i) const {return getConstTypedChild<Body>(i);}
Body* getParent() {return getTypedParent<Body>();}
const Body* getConstParent() const {return getConstTypedParent<Body>();}
Link* getLink() {return link;}
const Link* getConstLink() const {return link;}
const Body* getRoot() const {
RBXASSERT_SLOW(root == calcRootConst());
return root;
}
Body* getRoot() {
RBXASSERT_SLOW(root == calcRootConst());
return root;
}
const Vector3& getCofmOffset() {
return cofmOffset;
}
const Vector3& getBranchCofmOffset();
// Only works on bodies in same clump - otherwise not const for the link and will assert
CoordinateFrame getMeInAncestor(const Body* ancestor) const {
if (ancestor == this) {
return CoordinateFrame();
}
else if (ancestor == getConstParent()) {
return getConstMeInParent();
}
else {
return getConstParent()->getMeInAncestor(ancestor) * getConstMeInParent();
}
}
inline float getMass() const {return mass;}
inline Matrix3 getIBody() const {return moment;}
inline Vector3 getIBodyV3() const {return Math::toDiagonal(getIBody());}
Matrix3 getIBodyAtPoint(const Vector3& point);
inline Matrix3 getMoment() const {return getIBody();}
inline Vector3 getPrincipalMoment() const {return getIBodyV3();}
Matrix3 getIWorld() {return Math::momentToWorldSpace(getIBody(), getCoordinateFrame().rotation);}
Matrix3 getIWorldAtPoint(const Vector3& point);
// Branch refers to everything below me....
float getBranchMass() {return getCofm() ? getCofm()->getMass() : mass;}
Matrix3 getBranchIBody() {return getCofm() ? getCofm()->getMoment() : moment;}
Vector3 getBranchIBodyV3() {return Math::toDiagonal(getBranchIBody());}
Matrix3 getBranchIWorld() {return Math::momentToWorldSpace(getBranchIBody(), getCoordinateFrame().rotation);}
Matrix3 getBranchIWorldAtPoint(const Vector3& point);
Vector3 getBranchCofmPos();
CoordinateFrame getBranchCofmCoordinateFrame();
//
const PV& getPvFast() const {
RBXASSERT_FISHING(pvIsUpToDate());
return pv;
}
// Current Job should hold the Data Model write lock or somehow have locked the Body::mutex.
const PV& getPvUnsafe() {
updatePV();
return pv;
}
const PV& getPV_Spin_Lock() {
rbx::spin_mutex::scoped_lock lock(mutex);
updatePV();
return pv;
}
const PV& getPvSafe() const {
Body* thisNotConst = const_cast<Body*>(this);
return thisNotConst->getPV_Spin_Lock();
}
const Vector3& getPosFast() const {
RBXASSERT_FISHING(pvIsUpToDate());
return pv.position.translation;
}
const Vector3& getPos() {
updatePV();
return pv.position.translation;
}
const CoordinateFrame& getCoordinateFrameFast() const {
RBXASSERT_FISHING(pvIsUpToDate());
return pv.position;
}
const CoordinateFrame& getCoordinateFrame() {
updatePV();
return pv.position;
}
const Velocity& getVelocity() {
updatePV();
return pv.velocity;
}
bool getCanThrottle() const {
return canThrottle;
}
void accumulateImpulseAtBranchCofm(const Vector3& impulse) {
if (SimBody* s = getRootSimBody()) {
s->accumulateImpulseAtBranchCofm(impulse);
}
}
void accumulateLinearImpulse(const Vector3& impulse, const Vector3& worldPos) {
if (SimBody* s = getRootSimBody()) {
s->accumulateImpulse(impulse, worldPos);
}
}
void accumulateRotationalImpulse(const Vector3& impulse) {
if (SimBody* s = getRootSimBody()) {
s->accumulateRotationalImpulse(impulse);
}
}
void accumulateForceAtBranchCofm(const Vector3& force) {
RBXASSERT(getRoot() == this); // should only be called on Root objects
if (SimBody* s = getRootSimBody()) {
s->accumulateForceCofm(force);
}
}
void accumulateForce(const Vector3& force, const Vector3& worldPos) {
if (SimBody* s = getRootSimBody()) {
s->accumulateForce(force, worldPos);
}
}
void accumulateTorque(const Vector3& torque) {
if (SimBody* s = getRootSimBody()) {
s->accumulateTorque(torque);
}
}
void resetForceAccumulators() {
if (SimBody* s = getRootSimBody()) {
s->resetForceAccumulators();
}
}
void resetImpulseAccumulators() {
if (SimBody* s = getRootSimBody()) {
s->resetImpulseAccumulators();
}
}
const Vector3& getBranchForce() const {
RBXASSERT(getRoot() == this); // should only be called on Root objects
const SimBody* s = getConstRootSimBody();
return s ? s->getForce() : Vector3::zero();
}
const Vector3& getBranchTorque() const {
RBXASSERT(getRoot() == this); // should only be called on Root objects
const SimBody* s = getConstRootSimBody();
return s ? s->getTorque() : Vector3::zero();
}
const Velocity& getBranchVelocity() { // velocity at the COFM of the assembly
RBXASSERT(getRoot() == this); // should only be called on Root objects
const SimBody* s = getRootSimBody();
return s ? s->getPV().velocity : Velocity::zero();
}
/////////////////////////////////////////////////////
// setting properties
//
void setParent(Body* parent) {setIndexedTreeParent(parent);}
void setMeInParent(const CoordinateFrame& _meInParent);
void setMeInParent(Link* _link);
void setMass(float _mass);
void setMoment(const Matrix3& _momentInBody);
void setCofmOffset(const Vector3& _centerOfMassInBody);
// Only Primitive can set these - make sure we keep byproducts updated - fuzzy extents, etc.
// ToDo: Hack - is there a better way to limit access to only primitives, while not including "friend class Primitive"???
void setPv(const PV& _pv, const BodyPvSetter& bpv);
void setCoordinateFrame(const CoordinateFrame& worldCoord, const BodyPvSetter& bpv);
void setVelocity(const Velocity& worldVelocity, const BodyPvSetter& bpv);
void setCanThrottle(bool value, const BodyPvSetter& bpv);
void updateBulletCollisionObject(btCollisionObject* object);
public:
/////////////////////////////////////////////////////////
// Debugging / reporting functions / complex stuff
//
inline bool isLeafBody() const {return leafBodyIndex >= 0;}
float kineticEnergy();
float potentialEnergy();
};
} // namespace