2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/events/ray-test.h>
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>
29 using Dali::Internal::SceneGraph::Node;
38 : mEventThreadServices( EventThreadServices::Get() )
42 bool RayTest::SphereTest( const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir ) const
45 http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
47 Mathematical Formulation
49 Given the above mentioned sphere, a point 'p' lies on the surface of the sphere if
51 ( p - c ) dot ( p - c ) = r^2
53 Given a ray with a point of origin 'o', and a direction vector 'd':
55 ray(t) = o + td, t >= 0
57 we can find the t at which the ray intersects the sphere by setting ray(t) equal to 'p'
59 (o + td - c ) dot ( o + td - c ) = r^2
61 To solve for t we first expand the above into a more recognisable quadratic equation form
63 ( d dot d )t^2 + 2( o - c ) dot dt + ( o - c ) dot ( o - c ) - r^2 = 0
73 C = ( o - c ) dot ( o - c ) - r^2
75 which can be solved using a standard quadratic formula.
77 Note that in the absence of positive, real, roots, the ray does not intersect the sphere.
79 Practical Simplification
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.
85 If a sphere is centred at origin, a point 'p' lies on a sphere of radius r2 if
89 and we can find the t at which the (transformed) ray intersects the sphere by
91 ( o + td ) dot ( o + td ) = r^2
93 According to the reasoning above, we expand the above quadratic equation into the general form
97 which now has coefficients:
104 // Early out if not on the scene
105 if( ! actor.OnScene() )
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 );
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 );
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;
123 float squareSphereRadius = 0.5f * ( width * width + height * height );
125 float a = rayDir.Dot( rayDir ); // a
126 float b2 = rayDir.Dot( rayOriginLocal ); // b/2
127 float c = rayOriginLocal.Dot( rayOriginLocal ) - squareSphereRadius; // c
129 return ( b2 * b2 - a * c ) >= 0.0f;
133 bool RayTest::ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance) const
139 const Node& node = actor.GetNode();
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();
147 Vector4 rayOriginLocal(invModelMatrix * rayOrigin);
148 Vector4 rayDirLocal(invModelMatrix * rayDir - invModelMatrix.GetTranslation());
150 // Test with the actor's XY plane (Normal = 0 0 1 1).
151 float a = -rayOriginLocal.z;
152 float b = rayDirLocal.z;
154 if( fabsf( b ) > Math::MACHINE_EPSILON_1 )
156 // Ray travels distance * rayDirLocal to intersect with plane.
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;
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);
171 } // namespace Internal