Skip to content

Instantly share code, notes, and snippets.

@gszauer
Created April 10, 2025 17:18
Show Gist options
  • Save gszauer/1beee535a44768d061942490e6bd3141 to your computer and use it in GitHub Desktop.
Save gszauer/1beee535a44768d061942490e6bd3141 to your computer and use it in GitHub Desktop.
Object Rotator Prompt
Write a c++ class that rotates an object in 3d space. The camera is orthographic and a fixed distance away, you can only rotate the object.
Rotation should have smooth interpolation, giving it a mobile friendly feel.
smooth interpolation (even while draggin) is a must! The y axis should not be inverted.
This is NOT an arcball it needs to work anywhere the screen is touched. Go with a physics approach like a spring model rather than an arcball.
Take things like large or fast inputs, as well as suddent stops in input into account, and keep rotation smooth always. DAMPEN
This is the class you are implementing:
class ObjectRotator {
public:
inline void beginDrag(const glm::vec2& mouseScreenPos, const glm::vec2& viewportSize) {
// implement this
}
inline glm::quat drag(const glm::vec2& mouseScreenPos, const glm::vec2& viewportSize) {
// implement this. If you need delta time, its ImGui::GetIO().DeltaTime
// Return delta rotation to apply to object when dragging
}
inline glm::quat update(float deltaTime) {
// implement this
// Return delta rotation to apply to object when not dragging
}
inline bool endDrag() {
// implement this
}
float getSensitivity();
void setSensitivity(float value);
float getDampening();
void setDampening(float value);
// Max angular velocity, radians / second
float getMaxVelocity();
void setMaxVelocity(float value);
};
class ObjectRotator {
private:
glm::vec2 previousMousePos;
glm::vec3 angularVelocity = glm::vec3(0.0f);
bool isDragging = false;
float sensitivity = 8.0f;
float dampening = 6.0f; // Higher means faster slowdown
float maxVelocity = 14.0f;
public:
inline void beginDrag(const glm::vec2& mouseScreenPos, const glm::vec2& viewportSize) {
isDragging = true;
previousMousePos = mouseScreenPos;
angularVelocity = glm::vec3(0.0f); // Reset velocity when new drag starts
}
inline glm::quat drag(const glm::vec2& mouseScreenPos, const glm::vec2& viewportSize) {
const glm::vec2 currentPos = mouseScreenPos;
const glm::vec2 delta = currentPos - previousMousePos;
previousMousePos = currentPos;
// Convert pixel movement to normalized screen coordinates
const glm::vec2 deltaNormalized = delta / viewportSize;
// Calculate instantaneous velocity from this frame's movement
const float deltaTime = ImGui::GetIO().DeltaTime;
glm::vec3 frameVelocity(0.0f);
if (deltaTime > 0.0f) {
frameVelocity = glm::vec3(
deltaNormalized.y * sensitivity, // Pitch (X-axis)
deltaNormalized.x * sensitivity, // Yaw (Y-axis)
0.0f
) / deltaTime;
}
// During drag: set velocity directly based on current movement
angularVelocity = frameVelocity;
// Clamp velocity to maximum
const float currentSpeed = glm::length(angularVelocity);
if (currentSpeed > maxVelocity) {
angularVelocity = (angularVelocity / currentSpeed) * maxVelocity;
}
// Immediate rotation based on current drag
return quatFromAngularVelocity(deltaTime);
}
inline glm::quat update(float deltaTime) {
if (!isDragging) {
// Apply dampening when not dragging
angularVelocity *= glm::max(0.0f, 1.0f - dampening * deltaTime);
// Clamp to prevent tiny oscillations
if (glm::length(angularVelocity) < 0.01f) {
angularVelocity = glm::vec3(0.0f);
}
}
return quatFromAngularVelocity(deltaTime);
}
inline bool endDrag() {
isDragging = false;
return glm::length(angularVelocity) > 0.01f;
}
private:
inline glm::quat quatFromAngularVelocity(float deltaTime) const {
if (deltaTime <= 0.0f || glm::length(angularVelocity) < 1e-6f) {
return glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
}
const float angle = glm::length(angularVelocity) * deltaTime;
const glm::vec3 axis = glm::normalize(angularVelocity);
return glm::angleAxis(angle, axis);
}
public:
// Getters/setters remain the same
float getSensitivity() { return sensitivity; }
void setSensitivity(float value) { sensitivity = value; }
float getDampening() { return dampening; }
void setDampening(float value) { dampening = value; }
float getMaxVelocity() { return maxVelocity; }
void setMaxVelocity(float value) { maxVelocity = value; }
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment