1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
#pragma once
#include <container/seadBuffer.h>
#include <container/seadOffsetList.h>
#include <container/seadSafeArray.h>
#include <hostio/seadHostIONode.h>
#include <prim/seadSafeString.h>
#include <prim/seadScopedLock.h>
#include <thread/seadAtomic.h>
#include <thread/seadMutex.h>
#include <utility/aglParameter.h>
#include <utility/aglParameterIO.h>
#include <utility/aglParameterList.h>
#include <utility/aglParameterObj.h>
#include <utility/aglResParameter.h>
#include "KingSystem/Physics/physDefines.h"
#include "KingSystem/Physics/physMaterialMask.h"
#include "KingSystem/Utils/Types.h"
namespace sead {
class Heap;
}
namespace ksys::phys {
enum class IsIndoorStage;
struct CollidingBodies;
class CollisionInfo;
class ContactLayerCollisionInfo;
class ContactLayerCollisionInfoGroup;
class ContactPointInfoBase;
class QueryContactPointInfo;
class RigidBody;
struct RigidBodyCollisionMasks;
class ContactPointInfo;
class LayerContactPointInfo;
struct ContactInfoTable {
struct Receiver : agl::utl::ParameterObj {
const char* name = nullptr;
// TODO: figure out what these masks are
u32 layer_mask = 0;
u32 layer_mask2 = 0;
int num_layers = 0;
sead::SafeArray<agl::utl::Parameter<int>, MaxNumLayersPerType> layer_values;
protected:
void postRead_() override;
};
agl::utl::IParameterIO param_io;
agl::utl::ParameterList contact_info_table_plist;
sead::Buffer<Receiver> receivers;
};
struct ContactPoint {
enum class Flag {
/// Whether this contact point should be skipped when iterating over contact points.
Invalid = 1 << 0,
/// Whether the two bodies are penetrating.
Penetrating = 1 << 1,
};
RigidBody* body_a;
RigidBody* body_b;
sead::Vector3f position;
sead::Vector3f separating_normal;
float separating_distance;
MaterialMask material_mask_a;
MaterialMask material_mask_b;
u32 shape_key_a;
u32 shape_key_b;
sead::TypedBitFlag<Flag, u8> flags;
};
KSYS_CHECK_SIZE_NX150(ContactPoint, 0x70);
class ContactMgr : public sead::hostio::Node {
public:
struct ImpulseEntry;
ContactMgr();
virtual ~ContactMgr();
void init(sead::Heap* heap);
void initContactPointPool(sead::Heap* heap, IsIndoorStage indoor);
void freeContactPointPool();
void loadContactInfoTable(sead::Heap* heap, agl::utl::ResParameterArchive archive,
ContactLayerType type);
// region Factories for collision tracking structures
ContactPointInfo* makeContactPointInfo(sead::Heap* heap, int num, const sead::SafeString& name,
int a, int b, int c);
LayerContactPointInfo* makeLayerContactPointInfo(sead::Heap* heap, int num, int num2,
const sead::SafeString& name, int a, int b,
int c);
CollisionInfo* makeCollisionInfo(sead::Heap* heap, const sead::SafeString& name);
ContactLayerCollisionInfoGroup*
makeContactLayerCollisionInfoGroup(sead::Heap* heap, ContactLayer layer, int capacity,
const sead::SafeString& name);
// endregion
void registerContactPointInfo(ContactPointInfoBase* info);
void unregisterContactPointInfo(ContactPointInfoBase* info);
void freeContactPointInfo(ContactPointInfoBase* info);
void registerCollisionInfo(CollisionInfo* info);
void unregisterCollisionInfo(CollisionInfo* info);
void freeCollisionInfo(CollisionInfo* info);
void registerContactLayerCollisionInfoGroup(ContactLayerCollisionInfoGroup* group);
void unregisterContactLayerCollisionInfoGroup(ContactLayerCollisionInfoGroup* group);
void freeContactLayerCollisionInfoGroup(ContactLayerCollisionInfoGroup* group);
void clearContactPoints();
/// Remove all contact points with the specified rigid body.
/// @note For efficiency reasons, this actually only invalidates the contact points.
/// They will be removed at a later stage.
void removeContactPointsWithBody(RigidBody* body);
void removeCollisionEntriesWithBody(RigidBody* body);
void removeImpulseEntriesWithBody(RigidBody* body);
/// @param colliding_body_masks The collision masks of the colliding rigid bodies.
/// @returns whether contact should be disabled.
bool registerContactPoint(ContactPointInfo* info, const ContactPoint& point,
const RigidBodyCollisionMasks& colliding_body_masks,
bool penetrating);
bool registerContactPoint(QueryContactPointInfo* info, const ContactPoint& point,
bool penetrating);
void registerContactPoint(LayerContactPointInfo* info, const ContactPoint& point,
bool penetrating);
void registerCollision(CollisionInfo* info, RigidBody* body_a, RigidBody* body_b);
void registerCollision(ContactLayerCollisionInfo* info, RigidBody* body_a, RigidBody* body_b);
void unregisterCollision(CollisionInfo* info, RigidBody* body_a, RigidBody* body_b);
void unregisterCollision(ContactLayerCollisionInfo* info, RigidBody* body_a, RigidBody* body_b);
/// Unregister all collision pairs involving the specified rigid body.
void unregisterCollisionWithBody(ContactLayerCollisionInfo* info, RigidBody* body);
void clearCollisionEntries(CollisionInfo* info);
void clearCollisionEntries(ContactLayerCollisionInfoGroup* group);
bool initLayerMasks(ContactPointInfo* info, const sead::SafeString& receiver_name) const;
bool initLayerMasks(CollisionInfo* info, const sead::SafeString& receiver_name) const;
bool getSensorLayerMask(SensorCollisionMask* mask, const sead::SafeString& receiver_name) const;
/// Add a new impulse entry based on contact between the two specified rigid bodies.
/// After this is called, contact point information must then be specified using
/// setImpulseEntryContactInfo.
void addImpulseEntry(RigidBody* body_a, RigidBody* body_b);
/// Fill in an existing impulse entry with contact information.
/// This does nothing if there is no impulse entry for the specified rigid bodies.
void setImpulseEntryContactInfo(RigidBody* body_a, RigidBody* body_b,
const sead::Vector3f& contact_point_pos,
const sead::Vector3f& contact_point_normal, u32 material_a,
u32 material_b);
void processImpulseEntries();
void clearImpulseEntries();
private:
void processImpulseEntry(const ImpulseEntry& index);
void doLoadContactInfoTable(agl::utl::ResParameterArchive archive, ContactLayerType type,
bool skip_params);
/// @return an index into mContactPointPool or -1 if the pool is exhausted.
int allocateContactPoint();
void freeCollidingBodiesEntry(CollidingBodies* entry) {
auto lock = sead::makeScopedLock(mCollidingBodiesMutex);
mCollidingBodiesFreeList.pushBack(entry);
}
/// Used to optimise ContactPoint allocations.
sead::Buffer<ContactPoint> mContactPointPool;
sead::OffsetList<CollidingBodies> mCollidingBodiesFreeList;
int mCollidingBodiesCapacity = 0;
/// The number of contact points. Also the iindex of the next free ContactPoint in the pool.
sead::Atomic<int> mNumContactPoints = 0;
sead::OffsetList<ContactPointInfoBase> mContactPointInfoInstances;
sead::OffsetList<CollisionInfo> mCollisionInfoInstances;
sead::OffsetList<ContactLayerCollisionInfoGroup> mLayerColInfoGroups;
sead::OffsetList<ImpulseEntry> mImpulseEntriesFreeList;
sead::OffsetList<ImpulseEntry> mImpulseEntries;
sead::Mutex mContactPointInfoMutex;
sead::Mutex mCollisionInfoMutex;
sead::Mutex mLayerColInfoGroupMutex;
sead::Mutex mCollidingBodiesMutex;
sead::Mutex mImpulseEntriesMutex;
sead::SafeArray<ContactInfoTable, 2> mContactInfoTables{};
};
} // namespace ksys::phys
|