Created
February 22, 2019 00:45
-
-
Save JSandusky/d0e077b5527a07400bf11cfaad67a30e to your computer and use it in GitHub Desktop.
Motion Wheel ... WTFPL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MotionWheel::MotionWheel(Context* ctx) : Component(ctx), | |
traveledDistance_(0.0f), | |
lastPosition_(Vector3(M_INFINITY, M_INFINITY, M_INFINITY)) | |
{ | |
SubscribeToEvent(E_SCENEPOSTUPDATE, URHO3D_HANDLER(MotionWheel, OnUpdate)); | |
} | |
MotionWheel::~MotionWheel() | |
{ | |
} | |
void MotionWheel::RegisterObject(Context* context) | |
{ | |
context->RegisterFactory<MotionWheel>(IK_CATEGORY); | |
URHO3D_ATTRIBUTE("Diameter", float, wheelDiameter_, 1.0f, AM_DEFAULT); | |
URHO3D_ATTRIBUTE("Recording Dir", Vector3, recordingOrientation_, Vector3::FORWARD, AM_DEFAULT); | |
URHO3D_ATTRIBUTE("Bidirectional", bool, bidirectional_, true, AM_DEFAULT); | |
URHO3D_ATTRIBUTE("Is Constrained", bool, isConstrained_, true, AM_DEFAULT); | |
URHO3D_ATTRIBUTE("Ticks", unsigned, ticks_, 4, AM_DEFAULT); | |
URHO3D_ATTRIBUTE("Debug Offset", Vector3, debugDrawOffset_, Vector3::ZERO, AM_DEFAULT); | |
URHO3D_ATTRIBUTE("Traveled", float, traveledDistance_, 0.0f, AM_EDIT); | |
// diagnostic only | |
URHO3D_ATTRIBUTE("Delta", float, lastChange_, 0.0f, 0); | |
} | |
void MotionWheel::Reset(float traveled) | |
{ | |
SetTraveledDistance(traveled); | |
lastPosition_ = Vector3(M_INFINITY, M_INFINITY, M_INFINITY); | |
lastChange_ = 0.0f; | |
} | |
float MotionWheel::GetRevolutions() const | |
{ | |
return traveledDistance_ / (wheelDiameter_ * M_PI); | |
} | |
float MotionWheel::GetRevolutionFraction() const | |
{ | |
float baseVal = GetRevolutions(); | |
return baseVal - floorf(baseVal); | |
} | |
void MotionWheel::ResizeWheel(float newDiameter) | |
{ | |
const float fract = GetRevolutionFraction(); | |
wheelDiameter_ = newDiameter; | |
traveledDistance_ = GetCircumference() * fract; | |
} | |
void MotionWheel::OnUpdate(StringHash eventID, VariantMap& eventData) | |
{ | |
if (!IsEnabledEffective()) | |
return; | |
// record old distance so we can track that | |
const float oldDist = traveledDistance_; | |
auto worldPos = GetNode()->GetWorldPosition(); | |
if (lastPosition_.x_ != M_INFINITY) | |
{ | |
auto traveledVec = (worldPos - lastPosition_); | |
auto localTravelVec = GetNode()->GetWorldRotation().Inverse() * traveledVec; | |
const float len = localTravelVec.Length(); | |
localTravelVec.Normalize(); | |
recordingOrientation_.Normalize(); | |
float travelAccuracy = 1.0f; | |
if (isConstrained_) | |
travelAccuracy = bidirectional_ ? localTravelVec.AbsDotProduct(recordingOrientation_) : localTravelVec.DotProduct(recordingOrientation_); | |
if (len > M_LARGE_EPSILON && travelAccuracy > M_LARGE_EPSILON) | |
{ | |
travelAccuracy = Clamp01(travelAccuracy); | |
traveledDistance_ += len * travelAccuracy; | |
} | |
} | |
lastChange_ = traveledDistance_ - oldDist; | |
lastPosition_ = worldPos; | |
} | |
void MotionWheel::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) | |
{ | |
if (!IsEnabledEffective()) | |
return; | |
recordingOrientation_.Normalize(); | |
// find implicit tangent space | |
auto norm = recordingOrientation_; | |
Vector3 tangent, binormal; | |
if (norm.x_ > 0.5f || norm.y_ > 0.5f || norm.x_ < -0.5f || norm.y_ < -0.5f) | |
tangent = Vector3(norm.y_, -norm.x_, 0.0f); | |
else | |
tangent = Vector3(-norm.z_, 0.0f, norm.x_); | |
tangent.Normalize(); | |
auto worldRot = GetNode()->GetWorldRotation(); | |
norm = worldRot * norm; | |
auto worldPos = GetNode()->GetWorldPosition(); | |
worldPos += debugDrawOffset_; | |
auto worldRight = worldRot * tangent; | |
debug->AddCircle(worldPos, worldRight, wheelDiameter_ / 2.0f, Color::YELLOW, 32, false); | |
float startRot = GetRevolutionFraction() * 360.0f; | |
float stepPerTick = 360.0f / ticks_; | |
auto worldDown = norm; | |
for (unsigned i = 0; i < ticks_; ++i) | |
{ | |
float rot = startRot + stepPerTick * (i + 1); | |
Quaternion q; q.FromAngleAxis(rot, worldRight); | |
Vector3 lineVec = q * worldDown; | |
debug->AddLine(worldPos, worldPos + lineVec * wheelDiameter_, Color::YELLOW, false); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Wolfire style survey-wheel tracking. Doesn't manage animation in any way, only records traveled distance | |
/// and provides an easy way to get the fraction or total number of wheel revolutions. | |
/// Ticks are merely a visual aid to slice the wheel starting at with neutral 0 degrees at down. | |
class URHO3D_API MotionWheel : public Component | |
{ | |
URHO3D_OBJECT(MotionWheel, Component); | |
public: | |
MotionWheel(Context*); | |
virtual ~MotionWheel(); | |
static void RegisterObject(Context*); | |
void Reset(float traveled = 0.0f); | |
unsigned GetRevolutionsInt() const { return (int)floorf(GetRevolutions()); } | |
float GetRevolutions() const; | |
float GetRevolutionFraction() const; | |
float GetCircumference() const { return wheelDiameter_ * M_PI; } | |
float GetWheelDiameter() const { return wheelDiameter_; } | |
void SetWheelDiameter(float value) { wheelDiameter_ = value; } | |
/// Will change the size but remap the contained traveleddistance in order to match the correct fractional amount of the current value. | |
void ResizeWheel(float newDiameter); | |
float GetTraveledDistance() const { return traveledDistance_; } | |
void SetTraveledDistance(float dist) { traveledDistance_ = dist; } | |
unsigned GetTicks() const { return ticks_; } | |
void SetTicks(unsigned value) { ticks_ = value; } | |
Vector3 GetDebugDrawOffset() const { return debugDrawOffset_; } | |
void SetDebugDrawOffset(const Vector3& value) { debugDrawOffset_ = value; } | |
bool IsBidirectional() const { return bidirectional_; } | |
void SetBidirectional(bool value) { bidirectional_ = value; } | |
bool IsConstrained() const { return isConstrained_; } | |
void SetConstrained(bool value) { isConstrained_ = value; } | |
bool TravelHasChanged() const { return lastChange_ > M_LARGE_EPSILON; } | |
virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest) override; | |
private: | |
void OnUpdate(StringHash eventID, VariantMap& eventData); | |
/// Last recorded sample position. | |
Vector3 lastPosition_ = Vector3(M_INFINITY, M_INFINITY, M_INFINITY); | |
/// Local vector of travel to use, Forward records forward motion, Right records rightward motion, etc. | |
Vector3 recordingOrientation_ = Vector3::FORWARD; | |
/// Offset to apply to the wheel when debug-render is drawn. | |
Vector3 debugDrawOffset_ = Vector3::ZERO; | |
/// Diameter of the surveyers wheel. | |
float wheelDiameter_ = 2.0f; | |
/// Accumulated travel distance. | |
float traveledDistance_ = 0.0f; | |
/// Amount of change is the last update. | |
float lastChange_ = 0.0f; | |
/// Number of spokes to draw radiating out. | |
unsigned ticks_ = 4; | |
/// Does moving in reverse of the recording direction all count? | |
bool bidirectional_ = false; | |
/// Are we constrained to the recording direction? | |
bool isConstrained_ = true; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment