summaryrefslogtreecommitdiff
path: root/libs/ode-0.16.1/ode/src/collision_trimesh_ray.cpp
blob: 866758a1a6955c069be2618ee4bc7c612f877b07 (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
/*************************************************************************
 *                                                                       *
 * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
 * All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
 *                                                                       *
 * This library is free software; you can redistribute it and/or         *
 * modify it under the terms of EITHER:                                  *
 *   (1) The GNU Lesser General Public License as published by the Free  *
 *       Software Foundation; either version 2.1 of the License, or (at  *
 *       your option) any later version. The text of the GNU Lesser      *
 *       General Public License is included with this library in the     *
 *       file LICENSE.TXT.                                               *
 *   (2) The BSD-style license that is included with this library in     *
 *       the file LICENSE-BSD.TXT.                                       *
 *                                                                       *
 * This library 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 files    *
 * LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
 *                                                                       *
 *************************************************************************/

// TriMesh code by Erwin de Vries.

#include <ode/collision.h>
#include <ode/rotation.h>
#include "config.h"
#include "matrix.h"
#include "odemath.h"

#if dTRIMESH_ENABLED

#include "collision_util.h"
#include "collision_trimesh_internal.h"

#if dTRIMESH_OPCODE
int dCollideRTL(dxGeom* g1, dxGeom* RayGeom, int Flags, dContactGeom* Contacts, int Stride){
    dIASSERT (Stride >= (int)sizeof(dContactGeom));
    dIASSERT (g1->type == dTriMeshClass);
    dIASSERT (RayGeom->type == dRayClass);
    dIASSERT ((Flags & NUMC_MASK) >= 1);

    dxTriMesh* TriMesh = (dxTriMesh*)g1;

    const unsigned uiTLSKind = TriMesh->getParentSpaceTLSKind();
    dIASSERT(uiTLSKind == RayGeom->getParentSpaceTLSKind()); // The colliding spaces must use matching cleanup method
    TrimeshCollidersCache *pccColliderCache = GetTrimeshCollidersCache(uiTLSKind);
    RayCollider& Collider = pccColliderCache->m_RayCollider;

    dReal Length = dGeomRayGetLength(RayGeom);

    int FirstContact = dGeomRayGetFirstContact(RayGeom);
    int BackfaceCull = dGeomRayGetBackfaceCull(RayGeom);
    int ClosestHit = dGeomRayGetClosestHit(RayGeom);

    Collider.SetFirstContact(FirstContact != 0);
    Collider.SetClosestHit(ClosestHit != 0);
    Collider.SetCulling(BackfaceCull != 0);
    Collider.SetMaxDist(Length);

    const dVector3& TLPosition = *(const dVector3*)dGeomGetPosition(TriMesh);
    const dMatrix3& TLRotation = *(const dMatrix3*)dGeomGetRotation(TriMesh);

    Matrix4x4 MeshMatrix;
    const dVector3 ZeroVector3 = { REAL(0.0), };
    MakeMatrix(ZeroVector3, TLRotation, MeshMatrix);

    dVector3 Origin, Direction;
    dGeomRayGet(RayGeom, Origin, Direction);

    dVector3 OffsetOrigin;
    dSubtractVectors3(OffsetOrigin, Origin, TLPosition);

    /* Make Ray */
    Ray WorldRay;
    WorldRay.mOrig.Set(OffsetOrigin[0], OffsetOrigin[1], OffsetOrigin[2]);
    WorldRay.mDir.Set(Direction[0], Direction[1], Direction[2]);

    /* Intersect */
    int TriCount = 0;
    if (Collider.Collide(WorldRay, TriMesh->retrieveMeshBVTreeRef(), &MeshMatrix)) {
        TriCount = pccColliderCache->m_Faces.GetNbFaces();
    }

    if (TriCount == 0) {
        return 0;
    }

    const CollisionFace* Faces = pccColliderCache->m_Faces.GetFaces();

    int OutTriCount = 0;
    for (int i = 0; i < TriCount; i++) {
        if (TriMesh->m_RayCallback == null ||
            TriMesh->m_RayCallback(TriMesh, RayGeom, Faces[i].mFaceID,
            Faces[i].mU, Faces[i].mV)) {
                const int& TriIndex = Faces[i].mFaceID;
                if (!TriMesh->invokeCallback(RayGeom, TriIndex)) {
                    continue;
                }

                dContactGeom* Contact = SAFECONTACT(Flags, Contacts, OutTriCount, Stride);

                dVector3 dv[3];
                TriMesh->fetchMeshTriangle(dv, TriIndex, TLPosition, TLRotation);

                dVector3 vu;
                vu[0] = dv[1][0] - dv[0][0];
                vu[1] = dv[1][1] - dv[0][1];
                vu[2] = dv[1][2] - dv[0][2];
                vu[3] = REAL(0.0);

                dVector3 vv;
                vv[0] = dv[2][0] - dv[0][0];
                vv[1] = dv[2][1] - dv[0][1];
                vv[2] = dv[2][2] - dv[0][2];
                vv[3] = REAL(0.0);

                dCalcVectorCross3(Contact->normal, vv, vu);	// Reversed

                // Even though all triangles might be initially valid, 
                // a triangle may degenerate into a segment after applying 
                // space transformation.
                if (dSafeNormalize3(Contact->normal))
                {
                    // No sense to save on single type conversion in algorithm of this size.
                    // If there would be a custom typedef for distance type it could be used 
                    // instead of dReal. However using float directly is the loss of abstraction 
                    // and possible loss of precision in future.
                    /*float*/ dReal T = Faces[i].mDistance;
                    Contact->pos[0] = Origin[0] + (Direction[0] * T);
                    Contact->pos[1] = Origin[1] + (Direction[1] * T);
                    Contact->pos[2] = Origin[2] + (Direction[2] * T);
                    Contact->pos[3] = REAL(0.0);

                    Contact->depth = T;
                    Contact->g1 = TriMesh;
                    Contact->g2 = RayGeom;
                    Contact->side1 = TriIndex;
                    Contact->side2 = -1;

                    OutTriCount++;

                    // Putting "break" at the end of loop prevents unnecessary checks on first pass and "continue"
                    if (OutTriCount >= (Flags & NUMC_MASK)) {
                        break;
                    }
                }
        }
    }
    return OutTriCount;
}
#endif // dTRIMESH_OPCODE

#if dTRIMESH_GIMPACT
int dCollideRTL(dxGeom* g1, dxGeom* RayGeom, int Flags, dContactGeom* Contacts, int Stride)
{
    dIASSERT (Stride >= (int)sizeof(dContactGeom));
    dIASSERT (g1->type == dTriMeshClass);
    dIASSERT (RayGeom->type == dRayClass);
    dIASSERT ((Flags & NUMC_MASK) >= 1);

    dxTriMesh* TriMesh = (dxTriMesh*)g1;

    dReal Length = dGeomRayGetLength(RayGeom);
    int FirstContact = dGeomRayGetFirstContact(RayGeom);
    int BackfaceCull = dGeomRayGetBackfaceCull(RayGeom);
    int ClosestHit = dGeomRayGetClosestHit(RayGeom);
    dVector3 Origin, Direction;
    dGeomRayGet(RayGeom, Origin, Direction);

    char intersect=0;
    GIM_TRIANGLE_RAY_CONTACT_DATA contact_data;

    if(ClosestHit)
    {
        intersect = gim_trimesh_ray_closest_collisionODE(&TriMesh->m_collision_trimesh,Origin,Direction,Length,&contact_data);
    }
    else
    {
        intersect = gim_trimesh_ray_collisionODE(&TriMesh->m_collision_trimesh,Origin,Direction,Length,&contact_data);
    }

    if(intersect == 0)
    {
        return 0;
    }


    if(!TriMesh->m_RayCallback || 
        TriMesh->m_RayCallback(TriMesh, RayGeom, contact_data.m_face_id, contact_data.u , contact_data.v))
    {
        dContactGeom* Contact = &( Contacts[ 0 ] );
        VEC_COPY(Contact->pos,contact_data.m_point);
        VEC_COPY(Contact->normal,contact_data.m_normal);
        Contact->depth = contact_data.tparam;
        Contact->g1 = TriMesh;
        Contact->g2 = RayGeom;
        Contact->side1 = contact_data.m_face_id;
        Contact->side2 = -1;
        return 1;
    }

    return 0;
}
#endif  // dTRIMESH_GIMPACT

#endif // dTRIMESH_ENABLED