Ogre中的射线查询一般只能侦测到边界盒(AABB),就像你在下面图片看到的,使用下面的代码就能避免这些。
初始化射线查询:
1
2
3
4
5
6
7
8
|
// create the ray scene query object
m_pray_scene_query
=
m_pscene_manager
->
createRayQuery
(
Ogre
::
Ray
(
)
,
Ogre
::
SceneManager
::
WORLD_GEOMETRY_TYPE_MASK
)
;
if
(
NULL
==
m_pray_scene_query
)
{
LOG_ERROR
<<
"Failed to create Ogre::RaySceneQuery instance"
<<
ENDLOG
;
return
(
false
)
;
}
m_pray_scene_query
->
setSortByDistance
(
true
)
;
|
射线投射:
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
|
// raycast from a point in to the scene.
// returns success or failure.
// on success the point is returned in the result.
bool
OgreVisionEngine
::
RaycastFromPoint
(
const
Vector3
&
point
,
const
Vector3
&
normal
,
Vector3
&
result
)
{
// create the ray to test
Ogre
::
Ray
ray
(
Ogre
::
Vector3
(
point
.
x
,
point
.
y
,
point
.
z
)
,
Ogre
::
Vector3
(
normal
.
x
,
normal
.
y
,
normal
.
z
)
)
;
// check we are initialised
if
(
m_pray_scene_query
!=
NULL
)
{
// create a query object
m_pray_scene_query
->
setRay
(
ray
)
;
// execute the query, returns a vector of hits
if
(
m_pray_scene_query
->
execute
(
)
.
size
(
)
<=
0
)
{
// raycast did not hit an objects bounding box
return
(
false
)
;
}
}
else
{
LOG_ERROR
<<
"Cannot raycast without RaySceneQuery instance"
<<
ENDLOG
;
return
(
false
)
;
}
// at this point we have raycast to a series of different objects bounding boxes.
// we need to test these different objects to see which is the first polygon hit.
// there are some minor optimizations (distance based) that mean we wont have to
// check all of the objects most of the time, but the worst case scenario is that
// we need to test every triangle of every object.
Ogre
::
Real
closest_distance
=
-
1.0f
;
Ogre
::
Vector3
closest_result
;
Ogre
::
RaySceneQueryResult
&
query_result
=
m_pray_scene_query
->
getLastResults
(
)
;
for
(
size_t
qr_idx
=
0
;
qr_idx
<
query_result
.
size
(
)
;
qr_idx
++
)
{
// stop checking if we have found a raycast hit that is closer
// than all remaining entities
if
(
(
closest_distance
>=
0.0f
)
&&
(
closest_distance
<
query_result
[
qr_idx
]
.
distance
)
)
{
break
;
}
// only check this result if its a hit against an entity
if
(
(
query_result
[
qr_idx
]
.
movable
!=
NULL
)
&&
(
query_result
[
qr_idx
]
.
movable
->
getMovableType
(
)
.
compare
(
"Entity"
)
==
0
)
)
{
// get the entity to check
Ogre
::
Entity *
pentity
=
static_cast
<
Ogre
::
Entity*
>
(
query_result
[
qr_idx
]
.
movable
)
;
// mesh data to retrieve
size_t
vertex_count
;
size_t
index_count
;
Ogre
::
Vector3 *
vertices
;
unsigned
long
*
indices
;
// get the mesh information
OgreVE
::
GetMeshInformation
(
pentity
->
getMesh
(
)
,
vertex_count
,
vertices
,
index_count
,
indices
,
pentity
->
getParentNode
(
)
->
getWorldPosition
(
)
,
pentity
->
getParentNode
(
)
->
getWorldOrientation
(
)
,
pentity
->
getParentNode
(
)
->
_getDerivedScale
(
)
)
;
// test for hitting individual triangles on the mesh
bool
new_closest_found
=
false
;
for
(
int
i
=
0
;
i
<
static_cast
<
int
>
(
index_count
)
;
i
+=
3
)
{
// check for a hit against this triangle
std
::
pair
<
bool
,
Ogre
::
Real
>
hit
=
Ogre
::
Math
::
intersects
(
ray
,
vertices
[
indices
[
i
]
]
,
vertices
[
indices
[
i
+
1
]
]
,
vertices
[
indices
[
i
+
2
]
]
,
true
,
false
)
;
// if it was a hit check if its the closest
if
(
hit
.
first
)
{
if
(
(
closest_distance
<
0.0f
)
||
(
hit
.
second
<
closest_distance
)
)
{
// this is the closest so far, save it off
closest_distance
=
hit
.
second
;
new_closest_found
=
true
;
}
}
}
// free the verticies and indicies memory
delete
[
]
vertices
;
delete
[
]
indices
;
// if we found a new closest raycast for this object, update the
// closest_result before moving on to the next object.
if
(
new_closest_found
)
{
closest_result
=
ray
.
getPoint
(
closest_distance
)
;
}
}
}
// return the result
if
(
closest_distance
>=
0.0f
)
{
// raycast success
result
=
closest_result
;
return
(
true
)
;
}
else
{
// raycast failed
return
(
false
)
;
}
}
|
得到网格信息:
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
|
// Get the mesh information for the given mesh.
// Code found in Wiki: www.ogre3d.org/wiki/index.php/RetrieveVertexData
void
OgreVE
::
GetMeshInformation
(
const
Ogre
::
MeshPtr
mesh
,
size_t
&
vertex_count
,
Ogre
::
Vector3*
&
vertices
,
size_t
&
index_count
,
unsigned
long
*
&
indices
,
const
Ogre
::
Vector3
&
position
,
const
Ogre
::
Quaternion
&
orient
,
const
Ogre
::
Vector3
&
scale
)
{
bool
added_shared
=
false
;
size_t
current_offset
=
0
;
size_t
shared_offset
=
0
;
size_t
next_offset
=
0
;
size_t
index_offset
=
0
;
vertex_count
=
index_count
=
0
;
// Calculate how many vertices and indices we're going to need
for
(
unsigned
short
i
=
0
;
i
<
mesh
->
getNumSubMeshes
(
)
;
++
i
)
{
Ogre
::
SubMesh*
submesh
=
mesh
->
getSubMesh
(
i
)
;
// We only need to add the shared vertices once
if
(
submesh
->
useSharedVertices
)
{
if
(
!
added
_shared
)
{
vertex_count
+=
mesh
->
sharedVertexData
->
vertexCount
;
added_shared
=
true
;
}
}
else
{
vertex_count
+=
submesh
->
vertexData
->
vertexCount
;
}
// Add the indices
index_count
+=
submesh
->
indexData
->
indexCount
;
}
// Allocate space for the vertices and indices
vertices
=
new
Ogre
::
Vector3
[
vertex_count
]
;
indices
=
new
unsigned
long
[
index_count
]
;
added_shared
=
false
;
// Run through the submeshes again, adding the data into the arrays
for
(
unsigned
short
i
=
0
;
i
<
mesh
->
getNumSubMeshes
(
)
;
++
i
)
{
Ogre
::
SubMesh*
submesh
=
mesh
->
getSubMesh
(
i
)
;
Ogre
::
VertexData*
vertex_data
=
submesh
->
useSharedVertices
?
mesh
->
sharedVertexData
:
submesh
->
vertexData
;
if
(
(
!
submesh
->
useSharedVertices
)
||
(
submesh
->
useSharedVertices
&&
!
added_shared
)
)
{
if
(
submesh
->
useSharedVertices
)
{
added_shared
=
true
;
shared_offset
=
current_offset
;
}
const
Ogre
::
VertexElement*
posElem
=
vertex_data
->
vertexDeclaration
->
findElementBySemantic
(
Ogre
::
VES_POSITION
)
;
Ogre
::
HardwareVertexBufferSharedPtr
vbuf
=
vertex_data
->
vertexBufferBinding
->
getBuffer
(
posElem
->
getSource
(
)
)
;
unsigned
char
*
vertex
=
static_cast
<
unsigned
char
*
>
(
vbuf
->
lock
(
Ogre
::
HardwareBuffer
::
HBL_READ_ONLY
)
)
;
// There is _no_ baseVertexPointerToElement() which takes an Ogre::Real or a double
// as second argument. So make it float, to avoid trouble when Ogre::Real will
// be comiled/typedefed as double:
// Ogre::Real* pReal;
float
*
pReal
;
for
(
size
_t
j
=
0
;
j
<
vertex_data
->
vertexCount
;
++
j
,
vertex
+=
vbuf
->
getVertexSize
(
)
)
{
posElem
->
baseVertexPointerToElement
(
vertex
,
&
pReal
)
;
Ogre
::
Vector3
pt
(
pReal
[
0
]
,
pReal
[
1
]
,
pReal
[
2
]
)
;
vertices
[
current_offset
+
j
]
=
(
orient *
(
pt *
scale
)
)
+
position
;
}
vbuf
->
unlock
(
)
;
next_offset
+=
vertex_data
->
vertexCount
;
}
Ogre
::
IndexData*
index_data
=
submesh
->
indexData
;
size_t
numTris
=
index_data
->
indexCount
/
3
;
Ogre
::
HardwareIndexBufferSharedPtr
ibuf
=
index_data
->
indexBuffer
;
if
(
ibuf
.
isNull
(
)
)
continue
;
// need to check if index buffer is valid (which will be not if the mesh doesn't have triangles like a pointcloud)
bool
use32bitindexes
=
(
ibuf
->
getType
(
)
==
Ogre
::
HardwareIndexBuffer
::
IT_32BIT
)
;
unsigned
long
*
pLong
=
static_cast
<
unsigned
long
*
>
(
ibuf
->
lock
(
Ogre
::
HardwareBuffer
::
HBL_READ_ONLY
)
)
;
unsigned
short
*
pShort
=
reinterpret_cast
<
unsigned
short
*
>
(
pLong
)
;
size_t
offset
=
(
submesh
->
useSharedVertices
)
?
shared_offset
:
current_offset
;
size_t
index_start
=
index_data
->
indexStart
;
size_t
last_index
=
numTris*
3
+
index_start
;
if
(
use32bitindexes
)
for
(
size
_t
k
=
index_start
;
k
<
last_index
;
++
k
)
{
indices
[
index_offset
++
]
=
pLong
[
k
]
+
static_cast
<
unsigned
long
>
(
offset
)
;
}
else
for
(
size
_t
k
=
index_start
;
k
<
last_index
;
++
k
)
{
indices
[
index_offset
++
]
=
static_cast
<
unsigned
long
>
(
pShort
[
k
]
)
+
static_cast
<
unsigned
long
>
(
offset
)
;
}
ibuf
->
unlock
(
)
;
current_offset
=
next_offset
;
}
}
|
如果要考虑对处于运动状态物体做碰撞检测,可使用如下的得到网格信息代码(如果运动物体使用硬件蒙皮下面代码会出问题):
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
|
void
GetMeshInformation
(
const
Entity *
entity
,
size_t
&
vertex_count
,
Ogre
::
Vector3*
&
vertices
,
size_t
&
index_count
,
unsigned
long
*
&
indices
,
const
Ogre
::
Vector3
&
position
,
const
Ogre
::
Quaternion
&
orient
,
const
Ogre
::
Vector3
&
scale
)
{
bool
added_shared
=
false
;
size_t
current_offset
=
0
;
size_t
shared_offset
=
0
;
size_t
next_offset
=
0
;
size_t
index_offset
=
0
;
vertex_count
=
index_count
=
0
;
Ogre
::
MeshPtr
mesh
=
entity
->
getMesh
(
)
;
bool
useSoftwareBlendingVertices
=
entity
->
hasSkeleton
(
)
;
if
(
useSoftwareBlendingVertices
)
{
entity
->
_updateAnimation
(
)
;
}
// Calculate how many vertices and indices we're going to need
for
(
unsigned
short
i
=
0
;
i
<
mesh
->
getNumSubMeshes
(
)
;
++
i
)
{
Ogre
::
SubMesh*
submesh
=
mesh
->
getSubMesh
(
i
)
;
// We only need to add the shared vertices once
if
(
submesh
->
useSharedVertices
)
{
if
(
!
added
_shared
)
{
vertex_count
+=
mesh
->
sharedVertexData
->
vertexCount
;
added_shared
=
true
;
}
}
else
{
vertex_count
+=
submesh
->
vertexData
->
vertexCount
;
}
// Add the indices
index_count
+=
submesh
->
indexData
->
indexCount
;
}
// Allocate space for the vertices and indices
vertices
=
new
Ogre
::
Vector3
[
vertex_count
]
;
indices
=
new
unsigned
long
[
index_count
]
;
added_shared
=
false
;
// Run through the submeshes again, adding the data into the arrays
for
(
unsigned
short
i
=
0
;
i
<
mesh
->
getNumSubMeshes
(
)
;
++
i
)
{
Ogre
::
SubMesh*
submesh
=
mesh
->
getSubMesh
(
i
)
;
//----------------------------------------------------------------
// GET VERTEXDATA
//----------------------------------------------------------------
//Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
Ogre
::
VertexData*
vertex_data
;
//When there is animation:
if
(
useSoftwareBlendingVertices
)
vertex_data
=
submesh
->
useSharedVertices
?
entity
->
_getSkelAnimVertexData
(
)
:
entity
->
getSubEntity
(
i
)
->
_getSkelAnimVertexData
(
)
;
else
vertex_data
=
submesh
->
useSharedVertices
?
mesh
->
sharedVertexData
:
submesh
->
vertexData
;
if
(
(
!
submesh
->
useSharedVertices
)
||
(
submesh
->
useSharedVertices
&&
!
added_shared
)
)
{
if
(
submesh
->
useSharedVertices
)
{
added_shared
=
true
;
shared_offset
=
current_offset
;
}
const
Ogre
::
VertexElement*
posElem
=
vertex_data
->
vertexDeclaration
->
findElementBySemantic
(
Ogre
::
VES_POSITION
)
;
Ogre
::
HardwareVertexBufferSharedPtr
vbuf
=
vertex_data
->
vertexBufferBinding
->
getBuffer
(
posElem
->
getSource
(
)
)
;
unsigned
char
*
vertex
=
static_cast
<
unsigned
char
*
>
(
vbuf
->
lock
(
Ogre
::
HardwareBuffer
::
HBL_READ_ONLY
)
)
;
// There is _no_ baseVertexPointerToElement() which takes an Ogre::Real or a double
// as second argument. So make it float, to avoid trouble when Ogre::Real will
// be comiled/typedefed as double:
// Ogre::Real* pReal;
float
*
pReal
;
for
(
size
_t
j
=
0
;
j
<
vertex_data
->
vertexCount
;
++
j
,
vertex
+=
vbuf
->
getVertexSize
(
)
)
{
posElem
->
baseVertexPointerToElement
(
vertex
,
&
pReal
)
;
Ogre
::
Vector3
pt
(
pReal
[
0
]
,
pReal
[
1
]
,
pReal
[
2
]
)
;
vertices
[
current_offset
+
j
]
=
(
orient *
(
pt *
scale
)
)
+
position
;
}
vbuf
->
unlock
(
)
;
next_offset
+=
vertex_data
->
vertexCount
;
}
Ogre
::
IndexData*
index_data
=
submesh
->
indexData
;
size_t
numTris
=
index_data
->
indexCount
/
3
;
Ogre
::
HardwareIndexBufferSharedPtr
ibuf
=
index_data
->
indexBuffer
;
bool
use32bitindexes
=
(
ibuf
->
getType
(
)
==
Ogre
::
HardwareIndexBuffer
::
IT_32BIT
)
;
unsigned
long
*
pLong
=
static_cast
<
unsigned
long
*
>
(
ibuf
->
lock
(
Ogre
::
HardwareBuffer
::
HBL_READ_ONLY
)
)
;
unsigned
short
*
pShort
=
reinterpret_cast
<
unsigned
short
*
>
(
pLong
)
;
size_t
offset
=
(
submesh
->
useSharedVertices
)
?
shared_offset
:
current_offset
;
size_t
index_start
=
index_data
->
indexStart
;
size_t
last_index
=
numTris*
3
+
index_start
;
if
(
use32bitindexes
)
for
(
size
_t
k
=
index_start
;
k
<
last_index
;
++
k
)
{
indices
[
index_offset
++
]
=
pLong
[
k
]
+
static_cast
<
unsigned
long
>
(
offset
)
;
}
else
for
(
size
_t
k
=
index_start
;
k
<
last_index
;
++
k
)
{
indices
[
index_offset
++
]
=
static_cast
<
unsigned
long
>
(
pShort
[
k
]
)
+
static_cast
<
unsigned
long
>
(
offset
)
;
}
ibuf
->
unlock
(
)
;
current_offset
=
next_offset
;
}
}
|
总的原理是进行射线查询,然后获取模型mesh顶点与索引信息,将每三个顶点构成一个三角面,将定义好的射线与这些三角面进行相交,得到距离最近的交点,射线发射点与该交点距离即为最近距离,最后返回该最近距离。
具体代码使用大家可以参考MOC这个轻量级的碰撞检测库,官网上有示例,可以很容易的掌握。
- 本文固定链接: http://blog.jianchihu.net/ogre-polygon-raycast-detection.html
- 转载请注明: Jianchihu 2014年04月04日 于 Jianchihu 发表