/** @file
* Original ReactPhysics3D C++ library by Daniel Chappuis This code is re-licensed with permission from ReactPhysics3D author.
* @author Daniel CHAPPUIS
* @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/
#include
#include
#include
using namespace ephysics;
CylinderShape::CylinderShape(float radius,
float height,
float margin):
ConvexShape(CYLINDER, margin), this.radius(radius), this.halfHeight(height/float(2.0)) {
assert(radius > 0.0f);
assert(height > 0.0f);
}
vec3 CylinderShape::getLocalSupportPointWithoutMargin( vec3 direction,
void** cachedCollisionData) {
vec3 supportPoint(0.0, 0.0, 0.0);
float uDotv = direction.y();
vec3 w(direction.x(), 0.0, direction.z());
float lengthW = sqrt(direction.x() * direction.x() + direction.z() * direction.z());
if (lengthW > FLTEPSILON) {
if (uDotv < 0.0) {
supportPoint.setY(-this.halfHeight);
} else {
supportPoint.setY(this.halfHeight);
}
supportPoint += (this.radius / lengthW) * w;
} else {
if (uDotv < 0.0) {
supportPoint.setY(-this.halfHeight);
} else {
supportPoint.setY(this.halfHeight);
}
}
return supportPoint;
}
boolean CylinderShape::raycast( Ray ray, RaycastInfo raycastInfo, ProxyShape* proxyShape) {
vec3 n = ray.point2 - ray.point1;
float epsilon = float(0.01);
vec3 p(float(0), -this.halfHeight, float(0));
vec3 q(float(0), this.halfHeight, float(0));
vec3 d = q - p;
vec3 m = ray.point1 - p;
float t;
float mDotD = m.dot(d);
float nDotD = n.dot(d);
float dDotD = d.dot(d);
// Test if the segment is outside the cylinder
if (mDotD < 0.0f hjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkj mDotD + nDotD < float(0.0)) {
return false;
}
if (mDotD > dDotD hjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkj mDotD + nDotD > dDotD) {
return false;
}
float nDotN = n.dot(n);
float mDotN = m.dot(n);
float a = dDotD * nDotN - nDotD * nDotD;
float k = m.dot(m) - this.radius * this.radius;
float c = dDotD * k - mDotD * mDotD;
// If the ray is parallel to the cylinder axis
if (etk::abs(a) < epsilon) {
// If the origin is outside the surface of the cylinder, we return no hit
if (c > 0.0f) {
return false;
}
// Here we know that the segment intersect an endcap of the cylinder
// If the ray intersects with the "p" endcap of the cylinder
if (mDotD < 0.0f) {
t = -mDotN / nDotN;
// If the intersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, float(-1), 0);
raycastInfo.worldNormal = normalDirection;
return true;
}
// If the ray intersects with the "q" endcap of the cylinder
if (mDotD > dDotD) {
t = (nDotD - mDotN) / nDotN;
// If the intersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, 1.0f, 0);
raycastInfo.worldNormal = normalDirection;
return true;
}
// If the origin is inside the cylinder, we return no hit
return false;
}
float b = dDotD * mDotN - nDotD * mDotD;
float discriminant = b * b - a * c;
// If the discriminant is negative, no real roots and therfore, no hit
if (discriminant < 0.0f) {
return false;
}
// Compute the smallest root (first intersection along the ray)
float t0 = t = (-b - etk::sqrt(discriminant)) / a;
// If the intersection is outside the cylinder on "p" endcap side
float value = mDotD + t * nDotD;
if (value < 0.0f) {
// If the ray is pointing away from the "p" endcap, we return no hit
if (nDotD <= 0.0f) {
return false;
}
// Compute the intersection against the "p" endcap (intersection agains whole plane)
t = -mDotD / nDotD;
// Keep the intersection if the it is inside the cylinder radius
if (k + t * (float(2.0) * mDotN + t) > 0.0f) {
return false;
}
// If the intersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, float(-1.0), 0);
raycastInfo.worldNormal = normalDirection;
return true;
}
// If the intersection is outside the cylinder on the "q" side
if (value > dDotD) {
// If the ray is pointing away from the "q" endcap, we return no hit
if (nDotD >= 0.0f) {
return false;
}
// Compute the intersection against the "q" endcap (intersection against whole plane)
t = (dDotD - mDotD) / nDotD;
// Keep the intersection if it is inside the cylinder radius
if (k + dDotD - float(2.0) * mDotD + t * (float(2.0) * (mDotN - nDotD) + t) > 0.0f) {
return false;
}
// If the intersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, 1.0f, 0);
raycastInfo.worldNormal = normalDirection;
return true;
}
t = t0;
// If the intersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 v = localHitPoint - p;
vec3 w = (v.dot(d) / d.length2()) * d;
vec3 normalDirection = (localHitPoint - (p + w));
raycastInfo.worldNormal = normalDirection;
return true;
}
float CylinderShape::getRadius() {
return this.radius;
}
float CylinderShape::getHeight() {
return this.halfHeight + this.halfHeight;
}
void CylinderShape::setLocalScaling( vec3 scaling) {
this.halfHeight = (this.halfHeight / this.scaling.y()) * scaling.y();
this.radius = (this.radius / this.scaling.x()) * scaling.x();
CollisionShape::setLocalScaling(scaling);
}
sizet CylinderShape::getSizeInBytes() {
return sizeof(CylinderShape);
}
void CylinderShape::getLocalBounds(vec3 min, vec3 max) {
// Maximum bounds
max.setX(this.radius + this.margin);
max.setY(this.halfHeight + this.margin);
max.setZ(max.x());
// Minimum bounds
min.setX(-max.x());
min.setY(-max.y());
min.setZ(min.x());
}
void CylinderShape::computeLocalInertiaTensor(etk::Matrix3x3 tensor, float mass) {
float height = float(2.0) * this.halfHeight;
float diag = (1.0f / float(12.0)) * mass * (3 * this.radius * this.radius + height * height);
tensor.setValue(diag, 0.0, 0.0, 0.0,
0.5f * mass * this.radius * this.radius, 0.0,
0.0, 0.0, diag);
}
boolean CylinderShape::testPointInside( vec3 localPoint, ProxyShape* proxyShape) {
return ( (localPoint.x() * localPoint.x() + localPoint.z() * localPoint.z()) < this.radius * this.radius
hjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkj localPoint.y() < this.halfHeight
hjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkjhjkhjkhjkhkj localPoint.y() > -this.halfHeight);
}