112c03a41423c68e00a5c94d05cfbec07a155921
[platform/core/uifw/dali-core.git] / dali / internal / event / events / ray-test.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/ray-test.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/math/vector2.h>
23 #include <dali/public-api/math/vector3.h>
24 #include <dali/public-api/math/vector4.h>
25 #include <dali/internal/event/actors/actor-impl.h>
26 #include <dali/internal/event/common/event-thread-services.h>
27 #include <dali/internal/update/nodes/node.h>
28
29 using Dali::Internal::SceneGraph::Node;
30
31 namespace Dali
32 {
33
34 namespace Internal
35 {
36
37 RayTest::RayTest()
38 : mEventThreadServices( EventThreadServices::Get() )
39 {
40 }
41
42 bool RayTest::SphereTest( const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir ) const
43 {
44   /*
45    http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
46
47    Mathematical Formulation
48
49    Given the above mentioned sphere, a point 'p' lies on the surface of the sphere if
50
51    ( p - c ) dot ( p - c ) = r^2
52
53    Given a ray with a point of origin 'o', and a direction vector 'd':
54
55    ray(t) = o + td, t >= 0
56
57    we can find the t at which the ray intersects the sphere by setting ray(t) equal to 'p'
58
59    (o + td - c ) dot ( o + td - c ) = r^2
60
61    To solve for t we first expand the above into a more recognisable quadratic equation form
62
63    ( d dot d )t^2 + 2( o - c ) dot dt + ( o - c ) dot ( o - c ) - r^2 = 0
64
65    or
66
67    At2 + Bt + C = 0
68
69    where
70
71    A = d dot d
72    B = 2( o - c ) dot d
73    C = ( o - c ) dot ( o - c ) - r^2
74
75    which can be solved using a standard quadratic formula.
76
77    Note that in the absence of positive, real, roots, the ray does not intersect the sphere.
78
79    Practical Simplification
80
81    In a renderer, we often differentiate between world space and object space. In the object space
82    of a sphere it is centred at origin, meaning that if we first transform the ray from world space
83    into object space, the mathematical solution presented above can be simplified significantly.
84
85    If a sphere is centred at origin, a point 'p' lies on a sphere of radius r2 if
86
87    p dot p = r^2
88
89    and we can find the t at which the (transformed) ray intersects the sphere by
90
91    ( o + td ) dot ( o + td ) = r^2
92
93    According to the reasoning above, we expand the above quadratic equation into the general form
94
95    At2 + Bt + C = 0
96
97    which now has coefficients:
98
99    A = d dot d
100    B = 2( d dot o )
101    C = o dot o - r^2
102    */
103
104   // Early out if not on the scene
105   if( ! actor.OnScene() )
106   {
107     return false;
108   }
109
110   const Node& node = actor.GetNode();
111   const BufferIndex bufferIndex = EventThreadServices::Get().GetEventBufferIndex();
112   const Vector3& translation = node.GetWorldPosition( bufferIndex );
113   const Vector3& size = node.GetSize( bufferIndex );
114   const Vector3& scale = node.GetWorldScale( bufferIndex );
115
116   // Transforms the ray to the local reference system. As the test is against a sphere, only the translation and scale are needed.
117   const Vector3 rayOriginLocal( rayOrigin.x - translation.x, rayOrigin.y - translation.y, rayOrigin.z - translation.z );
118
119   // Computing the radius is not needed, a square radius is enough so can just use size but we do need to scale the sphere
120   const float width = size.width * scale.width;
121   const float height = size.height * scale.height;
122
123   float squareSphereRadius = 0.5f * ( width * width + height * height );
124
125   float a = rayDir.Dot( rayDir );                                       // a
126   float b2 = rayDir.Dot( rayOriginLocal );                              // b/2
127   float c = rayOriginLocal.Dot( rayOriginLocal ) - squareSphereRadius;  // c
128
129   return ( b2 * b2 - a * c ) >= 0.0f;
130 }
131
132
133 bool RayTest::ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance) const
134 {
135   bool hit = false;
136
137   if(actor.OnScene())
138   {
139     const Node& node = actor.GetNode();
140
141     // Transforms the ray to the local reference system.
142     // Calculate the inverse of Model matrix
143     Matrix invModelMatrix( false/*don't init*/);
144     invModelMatrix = node.GetWorldMatrix(0);
145     invModelMatrix.Invert();
146
147     Vector4 rayOriginLocal(invModelMatrix * rayOrigin);
148     Vector4 rayDirLocal(invModelMatrix * rayDir - invModelMatrix.GetTranslation());
149
150     // Test with the actor's XY plane (Normal = 0 0 1 1).
151     float a = -rayOriginLocal.z;
152     float b = rayDirLocal.z;
153
154     if( fabsf( b ) > Math::MACHINE_EPSILON_1 )
155     {
156       // Ray travels distance * rayDirLocal to intersect with plane.
157       distance = a / b;
158
159       const Vector2& size = actor.GetTouchDelegateArea() == Vector2::ZERO ? Vector2(node.GetSize(EventThreadServices::Get().GetEventBufferIndex())) : actor.GetTouchDelegateArea();
160       hitPointLocal.x = rayOriginLocal.x + rayDirLocal.x * distance + size.x * 0.5f;
161       hitPointLocal.y = rayOriginLocal.y + rayDirLocal.y * distance + size.y * 0.5f;
162
163       // Test with the actor's geometry.
164       hit = (hitPointLocal.x >= 0.f) && (hitPointLocal.x <= size.x) && (hitPointLocal.y >= 0.f) && (hitPointLocal.y <= size.y);
165     }
166   }
167
168   return hit;
169 }
170
171 } // namespace Internal
172
173 } // namespace Dali