summaryrefslogtreecommitdiff
path: root/src/KingSystem/Physics/System/physEntityGroupFilter.h
blob: 70a1fa23773b5cc7e8cccba9b1c36a8636f4a930 (plain)
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
205
206
207
208
209
210
211
212
213
214
215
216
#pragma once

#include <container/seadSafeArray.h>
#include <prim/seadBitUtil.h>
#include <prim/seadSafeString.h>
#include "KingSystem/Physics/System/physGroupFilter.h"
#include "KingSystem/Physics/physDefines.h"

namespace ksys::phys {

class EntitySystemGroupHandler : public SystemGroupHandler {
public:
    explicit EntitySystemGroupHandler(int i) : SystemGroupHandler(i, ContactLayerType::Entity) {}

    u32 makeCollisionFilterInfo(u32 info, ContactLayer layer, GroundHit ground_hit) override;
    u32 makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, bool unk) override;
    u32 makeRagdollCollisionFilterInfo(GroundHit ground_hit) override;
    bool m8() override;
};

class EntityGroupFilter : public GroupFilter {
    SEAD_RTTI_OVERRIDE(EntityGroupFilter, GroupFilter)
public:
    static EntityGroupFilter* make(ContactLayer::ValueType first, ContactLayer::ValueType last,
                                   sead::Heap* heap);

    EntityGroupFilter(ContactLayer::ValueType first, ContactLayer::ValueType last);
    ~EntityGroupFilter() override;

    hkBool isCollisionEnabled(const hkpCollidable& a, const hkpCollidable& b) const override;
    hkBool isCollisionEnabled(const hkpCollisionInput& input, const hkpCdBody& a,
                              const hkpCdBody& b, const hkpShapeContainer& bContainer,
                              hkpShapeKey bKey) const override;
    hkBool isCollisionEnabled(const hkpCollisionInput& input, const hkpCdBody& collectionBodyA,
                              const hkpCdBody& collectionBodyB,
                              const hkpShapeContainer& containerShapeA,
                              const hkpShapeContainer& containerShapeB, hkpShapeKey keyA,
                              hkpShapeKey keyB) const override;
    hkBool isCollisionEnabled(const hkpShapeRayCastInput& aInput,
                              const hkpShapeContainer& bContainer, hkpShapeKey bKey) const override;
    hkBool isCollisionEnabled(const hkpWorldRayCastInput& inputA,
                              const hkpCollidable& collidableB) const override;

    bool shouldContactNeverBeIgnored(ContactLayer layerA, ContactLayer layerB) override;
    u32 makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit) override;
    ContactLayer getCollisionFilterInfoLayer(u32 info) override;
    u32 makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, bool unk) override;
    GroundHit getQueryCollisionMaskGroundHit(u32 info) override;
    void getCollisionFilterInfoLayerAndGroundHit(u32 info, ContactLayer* layer,
                                                 GroundHit* ground_hit) override;
    const char* getCollisionFilterInfoLayerText(u32 info) override;
    void setLayerCustomMask(ContactLayer layer, u32 mask) override;
    u32 getCollisionFilterInfoGroupHandlerIdx(u32 info) override;

    virtual u32 makeRagdollCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit,
                                               u32 bone_index, u32 parent_bone_index);
    /// @param layer An entity layer
    virtual void setEntityLayerCollisionEnabledMask(ContactLayer layer, u32 mask);

private:
    /// Checks whether two entities are colliding.
    hkBool testCollisionForEntities(u32 infoA, u32 gh_mask) const;
    /// Checks whether a phantom and an entity are colliding with each other.
    hkBool testCollisionForPhantom(u32 infoPhantom, u32 infoB) const;
    /// Checks whether a ray cast and an entity are colliding with each other.
    hkBool testCollisionForRayCasting(u32 infoRayCast, u32 info) const;

    hkBool shouldHandleGroundCollision(u32 infoA, u32 infoB, ContactLayer::ValueType layerA,
                                       ContactLayer::ValueType layerB) const;
    hkBool shouldHandleWaterCollision(u32 infoA, u32 infoB, ContactLayer::ValueType layerA,
                                      ContactLayer::ValueType layerB) const;

    hkBool testLayerCollision(ContactLayer::ValueType a, ContactLayer::ValueType b) const {
        return m_collisionLookupTable[a] & (1 << b);
    }

    void doInitSystemGroupHandlerLists_(sead::Heap* heap) override;
    int getFreeListIndex(const SystemGroupHandler* handler) override;
    void doInit_(sead::Heap* heap) override;

    /// A ContactLayer::size() x ContactLayer::size() matrix such that M[i][j] indicates
    /// whether contact between layers i and layers j can be ignored.
    /// \see EntityContactListener and shouldContactNeverBeIgnored
    sead::SafeArray<u32, ContactLayer::size()> mLayersThatCanBeIgnored;
};

void receiverMaskEnableLayer(SensorCollisionMask* mask, ContactLayer layer);
bool receiverMaskGetSensorLayerMaskForType(SensorCollisionMask* mask,
                                           const sead::SafeString& receiver_type);
void receiverMaskSetSensorLayerMask(SensorCollisionMask* mask, u32 layer_mask);

u32 orEntityGroundHitMask(u32 mask, GroundHit type);
u32 orEntityGroundHitMask(u32 mask, const sead::SafeString& type);

/// Returns a new collision mask in ground hit mask mode.
/// @param mask A collision mask that has been built using orEntityGroundHitMask.
u32 makeEntityGroundHitMask(ContactLayer layer, u32 mask);

/// Returns a new collision mask with the specified layer.
/// Preserves ground hit mask mode.
u32 makeEntityCollisionMask(ContactLayer layer, u32 mask);

/// Updates the collision mask with the specified ground hit type (*not* mask).
u32 setEntityCollisionMaskGroundHit(GroundHit ground_hit, u32 mask);

inline u32 EntitySystemGroupHandler::makeCollisionFilterInfo(u32 info, ContactLayer layer,
                                                             GroundHit ground_hit) {
    const EntityCollisionMask current_info{info};
    EntityCollisionMask result;

    if (layer == ContactLayer::EntityRagdoll) {
        result.regular.layer.Init(layer);
        result.regular.ragdoll_bone_index.Init(current_info.regular.ragdoll_bone_index);
        result.regular.ragdoll_parent_bone_index.Init(
            current_info.regular.ragdoll_parent_bone_index);
        result.regular.group_handler_index.Init(getIndex());
        result.regular.ground_hit.Init(ground_hit);
        result.is_ragdoll = true;
    } else {
        result.regular.layer.Init(layer);
        result.ground_col_mode.Init(current_info.ground_col_mode);
        result.regular.group_handler_index.Init(getIndex());
        result.regular.ground_hit.Init(ground_hit);
    }
    return result.raw;
}

inline u32 EntitySystemGroupHandler::makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit,
                                                            bool unk) {
    EntityQueryCollisionMask mask;
    mask.layer_mask = layer_mask;
    mask.group_handler_index.Init(getIndex());
    mask.ground_hit_type.Init(static_cast<GroundHit::ValueType>(int(ground_hit)));
    mask.unk.SetBit(unk);
    return mask.raw;
}

inline u32 EntitySystemGroupHandler::makeRagdollCollisionFilterInfo(GroundHit ground_hit) {
    EntityCollisionMask info;
    info.regular.layer.Init(ContactLayer::EntityRagdoll);
    info.regular.group_handler_index.Init(getIndex());
    info.regular.ground_hit.Init(ground_hit);
    return info.raw;
}

inline bool EntitySystemGroupHandler::m8() {
    return getIndex() > 0 && getIndex() < 0x400;
}

inline bool EntityGroupFilter::shouldContactNeverBeIgnored(ContactLayer layerA,
                                                           ContactLayer layerB) {
    return (mLayersThatCanBeIgnored[layerA.value()] & (1 << layerB.value())) == 0;
}

inline u32 EntityGroupFilter::makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit) {
    return EntityCollisionMask::make(layer, ground_hit).raw;
}

inline ContactLayer EntityGroupFilter::getCollisionFilterInfoLayer(u32 info) {
    return EntityCollisionMask(info).getLayer();
}

inline u32 EntityGroupFilter::makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit,
                                                     bool unk) {
    EntityQueryCollisionMask mask;
    mask.layer_mask = layer_mask;
    mask.ground_hit_type = ground_hit.value();
    mask.unk.SetBit(unk);
    return mask.raw;
}

inline GroundHit EntityGroupFilter::getQueryCollisionMaskGroundHit(u32 info) {
    return EntityQueryCollisionMask(info).ground_hit_type.Value();
}

inline void EntityGroupFilter::getCollisionFilterInfoLayerAndGroundHit(u32 info,
                                                                       ContactLayer* layer,
                                                                       GroundHit* ground_hit) {
    EntityCollisionMask info_{info};
    *layer = info_.getLayer();
    *ground_hit = info_.getGroundHit();
}

inline const char* EntityGroupFilter::getCollisionFilterInfoLayerText(u32 info) {
    EntityCollisionMask info_{info};
    if (info_.is_ground_hit_mask) {
        return "GroundHitMaskMode";
    }
    return contactLayerToText(getCollisionFilterInfoLayer(info));
}

inline void EntityGroupFilter::setLayerCustomMask(ContactLayer layer, u32 mask) {
    mLayersThatCanBeIgnored[layer] = mask;
}

inline u32 EntityGroupFilter::getCollisionFilterInfoGroupHandlerIdx(u32 info) {
    return EntityCollisionMask(info).regular.group_handler_index;
}

inline u32 EntityGroupFilter::makeRagdollCollisionFilterInfo(ContactLayer layer,
                                                             GroundHit ground_hit, u32 bone_index,
                                                             u32 parent_bone_index) {
    EntityCollisionMask info;
    info.regular.layer.Init(layer);
    info.regular.ragdoll_bone_index.Init(bone_index);
    info.regular.ragdoll_parent_bone_index.Init(parent_bone_index);
    info.regular.ground_hit.Init(ground_hit);
    info.is_ragdoll = true;
    return info.raw;
}

inline void EntityGroupFilter::setEntityLayerCollisionEnabledMask(ContactLayer layer, u32 mask) {
    m_collisionLookupTable[layer] = mask;
}

}  // namespace ksys::phys