Improve viewer camera controls and playback seeking
This commit is contained in:
@@ -13,6 +13,10 @@ constexpr float PitchLimit = 1.45f;
|
||||
constexpr float MinDistance = 0.05f;
|
||||
constexpr float OrbitSensitivity = 0.008f;
|
||||
constexpr float ZoomSensitivity = 0.15f;
|
||||
constexpr Deg VerticalFieldOfView = 35.0_degf;
|
||||
constexpr float FitPadding = 1.15f;
|
||||
constexpr float DefaultYaw = 0.78539816f;
|
||||
constexpr float DefaultPitch = 0.5f;
|
||||
}
|
||||
|
||||
void OrbitCameraController::setViewport(const Vector2i& windowSize, const Vector2i& framebufferSize) {
|
||||
@@ -23,9 +27,9 @@ void OrbitCameraController::setViewport(const Vector2i& windowSize, const Vector
|
||||
void OrbitCameraController::setSceneBounds(const Vector3& center, float radius) {
|
||||
_pivot = center;
|
||||
_sceneRadius = Math::max(radius, 0.1f);
|
||||
_distance = Math::max(_sceneRadius*2.5f, 0.5f);
|
||||
_yaw = 0.0f;
|
||||
_pitch = 0.35f;
|
||||
_distance = Math::max((_sceneRadius/Math::sin(VerticalFieldOfView*0.5f))*FitPadding, 0.5f);
|
||||
_yaw = DefaultYaw;
|
||||
_pitch = DefaultPitch;
|
||||
}
|
||||
|
||||
void OrbitCameraController::reset() {
|
||||
@@ -34,7 +38,8 @@ void OrbitCameraController::reset() {
|
||||
|
||||
void OrbitCameraController::orbit(const Vector2& deltaPixels) {
|
||||
_yaw -= deltaPixels.x()*OrbitSensitivity;
|
||||
_pitch = Math::clamp(_pitch - deltaPixels.y()*OrbitSensitivity, -PitchLimit, PitchLimit);
|
||||
const float verticalDirection = _invertY ? 1.0f : -1.0f;
|
||||
_pitch = Math::clamp(_pitch + verticalDirection*deltaPixels.y()*OrbitSensitivity, -PitchLimit, PitchLimit);
|
||||
}
|
||||
|
||||
void OrbitCameraController::pan(const Vector2& deltaPixels) {
|
||||
@@ -50,7 +55,7 @@ void OrbitCameraController::zoom(float wheelDelta) {
|
||||
}
|
||||
|
||||
Matrix4 OrbitCameraController::cameraTransform() const {
|
||||
return Matrix4::lookAt(position(), _pivot, Vector3::yAxis()).invertedRigid();
|
||||
return Matrix4::lookAt(position(), _pivot, Vector3::yAxis());
|
||||
}
|
||||
|
||||
Vector3 OrbitCameraController::position() const {
|
||||
@@ -64,7 +69,7 @@ Vector3 OrbitCameraController::position() const {
|
||||
}
|
||||
|
||||
float OrbitCameraController::panScale() const {
|
||||
return 2.0f*_distance*Math::tan(35.0_degf/2.0f)/Float(_windowSize.y());
|
||||
return 2.0f*_distance*Math::tan(VerticalFieldOfView*0.5f)/Float(_windowSize.y());
|
||||
}
|
||||
|
||||
Vector3 OrbitCameraController::forward() const {
|
||||
|
||||
@@ -10,6 +10,7 @@ class OrbitCameraController {
|
||||
public:
|
||||
void setViewport(const Magnum::Vector2i& windowSize, const Magnum::Vector2i& framebufferSize);
|
||||
void setSceneBounds(const Magnum::Vector3& center, float radius);
|
||||
void setInvertY(bool invertY) { _invertY = invertY; }
|
||||
void reset();
|
||||
|
||||
void orbit(const Magnum::Vector2& deltaPixels);
|
||||
@@ -33,7 +34,7 @@ private:
|
||||
float _yaw = 0.0f;
|
||||
float _pitch = 0.35f;
|
||||
float _sceneRadius = 1.0f;
|
||||
bool _invertY = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
+208
-22
@@ -6,10 +6,15 @@
|
||||
|
||||
#include <Magnum/GL/DefaultFramebuffer.h>
|
||||
#include <Magnum/GL/Renderer.h>
|
||||
#include <Magnum/MeshTools/Compile.h>
|
||||
#include <Magnum/Primitives/Cube.h>
|
||||
#include <Magnum/Primitives/Grid.h>
|
||||
#include <Magnum/Trade/MeshData.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
@@ -51,14 +56,16 @@ std::shared_ptr<CpuMesh> buildCpuMesh(const SimTK::PolygonalMesh& mesh) {
|
||||
}
|
||||
}
|
||||
|
||||
bool hasBounds = false;
|
||||
Range3D bounds;
|
||||
bounds.min() = Vector3{std::numeric_limits<Float>::max()};
|
||||
bounds.max() = Vector3{-std::numeric_limits<Float>::max()};
|
||||
cpu->vertices.reserve(positions.size());
|
||||
for(std::size_t i = 0; i != positions.size(); ++i) {
|
||||
const Vector3 normal = normals[i].isZero() ? Vector3::yAxis() : normals[i].normalized();
|
||||
cpu->vertices.push_back({positions[i], normal});
|
||||
bounds = Math::join(bounds, Range3D::fromSize(positions[i], {}));
|
||||
const Range3D point = Range3D::fromSize(positions[i], {});
|
||||
if(hasBounds) bounds = Math::join(bounds, point);
|
||||
else bounds = point;
|
||||
hasBounds = true;
|
||||
}
|
||||
cpu->bounds = bounds;
|
||||
return cpu;
|
||||
@@ -68,6 +75,48 @@ Matrix4 makeFrameMatrix(const Vector3& translation, const Quaternion& rotation)
|
||||
return Matrix4::from(rotation.toMatrix(), translation);
|
||||
}
|
||||
|
||||
std::shared_ptr<CpuMesh> buildBoxMesh() {
|
||||
auto cpu = std::make_shared<CpuMesh>();
|
||||
|
||||
const struct Face {
|
||||
Vector3 normal;
|
||||
Vector3 vertices[4];
|
||||
} faces[] = {
|
||||
{ Vector3::xAxis(), {
|
||||
{ 1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f, -1.0f }, { 1.0f, 1.0f, 1.0f }, { 1.0f, -1.0f, 1.0f }
|
||||
} },
|
||||
{ -Vector3::xAxis(), {
|
||||
{ -1.0f, -1.0f, 1.0f }, { -1.0f, 1.0f, 1.0f }, { -1.0f, 1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f }
|
||||
} },
|
||||
{ Vector3::yAxis(), {
|
||||
{ -1.0f, 1.0f, -1.0f }, { -1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, -1.0f }
|
||||
} },
|
||||
{ -Vector3::yAxis(), {
|
||||
{ -1.0f, -1.0f, 1.0f }, { -1.0f, -1.0f, -1.0f }, { 1.0f, -1.0f, -1.0f }, { 1.0f, -1.0f, 1.0f }
|
||||
} },
|
||||
{ Vector3::zAxis(), {
|
||||
{ -1.0f, -1.0f, 1.0f }, { 1.0f, -1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f }, { -1.0f, 1.0f, 1.0f }
|
||||
} },
|
||||
{ -Vector3::zAxis(), {
|
||||
{ 1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f }, { -1.0f, 1.0f, -1.0f }, { 1.0f, 1.0f, -1.0f }
|
||||
} },
|
||||
};
|
||||
|
||||
cpu->vertices.reserve(24);
|
||||
cpu->indices.reserve(36);
|
||||
for(const Face& face: faces) {
|
||||
const UnsignedInt base = UnsignedInt(cpu->vertices.size());
|
||||
for(const Vector3& position: face.vertices) cpu->vertices.push_back({position, face.normal});
|
||||
cpu->indices.insert(cpu->indices.end(), {
|
||||
base + 0, base + 1, base + 2,
|
||||
base + 0, base + 2, base + 3
|
||||
});
|
||||
}
|
||||
|
||||
cpu->bounds = Range3D{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
return cpu;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ViewerApp::MeshDrawable::MeshDrawable(Object3D& object,
|
||||
@@ -91,11 +140,32 @@ void ViewerApp::MeshDrawable::draw(const Matrix4& transformation, Camera3D& came
|
||||
.draw(_mesh.mesh);
|
||||
}
|
||||
|
||||
ViewerApp::FlatDrawable::FlatDrawable(Object3D& object,
|
||||
Shaders::FlatGL3D& shader,
|
||||
GL::Mesh& mesh,
|
||||
Color4 color,
|
||||
DrawableGroup3D& drawables):
|
||||
Drawable3D{object, &drawables},
|
||||
_shader{shader},
|
||||
_mesh{mesh},
|
||||
_color{color} {}
|
||||
|
||||
void ViewerApp::FlatDrawable::draw(const Matrix4& transformation, Camera3D& camera) {
|
||||
_shader
|
||||
.setColor(_color)
|
||||
.setTransformationProjectionMatrix(camera.projectionMatrix()*transformation)
|
||||
.draw(_mesh);
|
||||
}
|
||||
|
||||
ViewerApp::ViewerApp(const Arguments& arguments, CliOptions options):
|
||||
Platform::Application{arguments, Configuration{}.setTitle("osim-magnum-viewer").setWindowFlags(Configuration::WindowFlag::Resizable)},
|
||||
Platform::Application{arguments, Configuration{}
|
||||
.setTitle("osim-magnum-viewer")
|
||||
.setSize({800, 600}, Configuration::DpiScalingPolicy::Physical)
|
||||
.setWindowFlags(Configuration::WindowFlag::Resizable)},
|
||||
_options{std::move(options)},
|
||||
_sceneData{loadScene(_options.modelPath, _options.motionPath, _options.geometryDirs)},
|
||||
_shader{Shaders::PhongGL{}},
|
||||
_flatShader{Shaders::FlatGL3D{}},
|
||||
_isPlaying{!_options.startPaused},
|
||||
_playbackSpeed{_options.initialSpeed} {
|
||||
|
||||
@@ -112,14 +182,11 @@ ViewerApp::ViewerApp(const Arguments& arguments, CliOptions options):
|
||||
.setViewport(GL::defaultFramebuffer.viewport().size());
|
||||
|
||||
_orbit.setViewport(windowSize(), framebufferSize());
|
||||
const Range3D bounds = _sceneData.initialBounds;
|
||||
const Vector3 center = (bounds.min() + bounds.max())*0.5f;
|
||||
const float radius = Math::max((bounds.max() - bounds.min()).max(), 0.5f);
|
||||
_orbit.setSceneBounds(center, radius);
|
||||
_cameraObject.setTransformation(_orbit.cameraTransform());
|
||||
_orbit.setInvertY(!_options.reverseMouseY);
|
||||
if(!_sceneData.times.empty()) _currentTime = _sceneData.times.front();
|
||||
|
||||
createSceneObjects();
|
||||
resetDefaultCamera();
|
||||
updateSceneAtCurrentTime();
|
||||
|
||||
_lastTick = std::chrono::steady_clock::now();
|
||||
@@ -145,6 +212,11 @@ void ViewerApp::createSceneObjects() {
|
||||
auto markerCpu = buildCpuMesh(SimTK::PolygonalMesh::createSphereMesh(1.0, 1));
|
||||
_markerMesh = uploadMesh(*markerCpu);
|
||||
_gpuMeshes.push_back(_markerMesh);
|
||||
auto boxCpu = buildBoxMesh();
|
||||
_boxMesh = uploadMesh(*boxCpu);
|
||||
_gpuMeshes.push_back(_boxMesh);
|
||||
_groundGridMesh = MeshTools::compile(Primitives::grid3DWireframe({20, 20}));
|
||||
_originWireBoxMesh = MeshTools::compile(Primitives::cubeWireframe());
|
||||
|
||||
for(const FrameTrack& frameTrack: _sceneData.frames) {
|
||||
auto frameObject = std::make_unique<Object3D>(&_scene);
|
||||
@@ -183,6 +255,52 @@ void ViewerApp::createSceneObjects() {
|
||||
_objectStorage.push_back(std::move(markerObject));
|
||||
_drawableStorage.push_back(std::move(drawable));
|
||||
}
|
||||
|
||||
createReferenceObjects();
|
||||
}
|
||||
|
||||
void ViewerApp::createReferenceObjects() {
|
||||
const float radius = sceneRadius();
|
||||
|
||||
const float axisLength = Math::max(radius*0.6f, 0.35f);
|
||||
const float axisHalfLength = axisLength*0.5f;
|
||||
const float axisThickness = Math::max(radius*0.015f, 0.008f);
|
||||
const float groundScale = Math::max(radius*2.5f, 1.5f);
|
||||
const float originMarkerScale = Math::max(radius*0.04f, 0.03f);
|
||||
|
||||
const struct AxisSpec {
|
||||
Vector3 translation;
|
||||
Vector3 scale;
|
||||
Color4 color;
|
||||
} axes[] = {
|
||||
{ { axisHalfLength, 0.0f, 0.0f }, { axisHalfLength, axisThickness, axisThickness }, { 0.92f, 0.28f, 0.28f, 1.0f } },
|
||||
{ { 0.0f, axisHalfLength, 0.0f }, { axisThickness, axisHalfLength, axisThickness }, { 0.3f, 0.84f, 0.38f, 1.0f } },
|
||||
{ { 0.0f, 0.0f, axisHalfLength }, { axisThickness, axisThickness, axisHalfLength }, { 0.36f, 0.58f, 0.92f, 1.0f } },
|
||||
};
|
||||
|
||||
for(const AxisSpec& axis: axes) {
|
||||
auto object = std::make_unique<Object3D>(&_scene);
|
||||
object->setTransformation(Matrix4::translation(axis.translation)*Matrix4::scaling(axis.scale));
|
||||
auto drawable = std::make_unique<MeshDrawable>(*object, _shader, *_boxMesh, axis.color, _drawables);
|
||||
_drawableStorage.push_back(std::move(drawable));
|
||||
_objectStorage.push_back(std::move(object));
|
||||
}
|
||||
|
||||
auto originObject = std::make_unique<Object3D>(&_scene);
|
||||
originObject->setTransformation(Matrix4::scaling(Vector3{originMarkerScale}));
|
||||
auto originDrawable = std::make_unique<FlatDrawable>(
|
||||
*originObject, _flatShader, _originWireBoxMesh, Color4{0.82f, 0.84f, 0.88f, 1.0f}, _drawables);
|
||||
_drawableStorage.push_back(std::move(originDrawable));
|
||||
_objectStorage.push_back(std::move(originObject));
|
||||
|
||||
auto groundObject = std::make_unique<Object3D>(&_scene);
|
||||
groundObject->setTransformation(
|
||||
Matrix4::rotationX(90.0_degf)*
|
||||
Matrix4::scaling(Vector3{groundScale}));
|
||||
auto groundDrawable = std::make_unique<FlatDrawable>(
|
||||
*groundObject, _flatShader, _groundGridMesh, Color4{0.42f, 0.45f, 0.48f, 1.0f}, _drawables);
|
||||
_drawableStorage.push_back(std::move(groundDrawable));
|
||||
_objectStorage.push_back(std::move(groundObject));
|
||||
}
|
||||
|
||||
void ViewerApp::updatePlayback() {
|
||||
@@ -193,10 +311,16 @@ void ViewerApp::updatePlayback() {
|
||||
if(!_isPlaying || _sceneData.times.empty()) return;
|
||||
|
||||
_currentTime += delta*_playbackSpeed;
|
||||
const float startTime = _sceneData.times.front();
|
||||
const float endTime = _sceneData.times.back();
|
||||
const float duration = endTime - startTime;
|
||||
if(_currentTime >= endTime) {
|
||||
_currentTime = endTime;
|
||||
_isPlaying = false;
|
||||
if(_loopPlayback && duration > 0.0f) {
|
||||
_currentTime = startTime + std::fmod(_currentTime - startTime, duration);
|
||||
} else {
|
||||
_currentTime = endTime;
|
||||
_isPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,21 +360,84 @@ void ViewerApp::drawUi() {
|
||||
ImGui::TextUnformatted(_sceneData.modelName.c_str());
|
||||
ImGui::TextUnformatted(_sceneData.motionName.c_str());
|
||||
if(ImGui::Button(_isPlaying ? "Pause" : "Play")) {
|
||||
if(!_isPlaying && !_sceneData.times.empty() && _currentTime >= _sceneData.times.back()) _currentTime = 0.0f;
|
||||
if(!_isPlaying && !_sceneData.times.empty() && _currentTime >= _sceneData.times.back()) rewindPlayback();
|
||||
_isPlaying = !_isPlaying;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Rewind")) {
|
||||
_currentTime = 0.0f;
|
||||
_isPlaying = false;
|
||||
rewindPlayback();
|
||||
}
|
||||
ImGui::SliderFloat("Speed", &_playbackSpeed, 0.1f, 4.0f, "%.2fx");
|
||||
ImGui::Checkbox("Loop", &_loopPlayback);
|
||||
if(ImGui::Checkbox("Reverse mouse Y", &_options.reverseMouseY)) {
|
||||
_orbit.setInvertY(!_options.reverseMouseY);
|
||||
}
|
||||
if(!_sceneData.times.empty()) {
|
||||
float seekTime = _currentTime;
|
||||
if(ImGui::SliderFloat("Seek", &seekTime, _sceneData.times.front(), _sceneData.times.back(), "%.3f",
|
||||
ImGuiSliderFlags_AlwaysClamp)) {
|
||||
seekPlayback(seekTime);
|
||||
}
|
||||
}
|
||||
ImGui::Text("Time %.3f / %.3f", _currentTime, _sceneData.times.empty() ? 0.0f : _sceneData.times.back());
|
||||
ImGui::Text("Frames %zu Markers %zu", _sceneData.frames.size(), _sceneData.markers.size());
|
||||
ImGui::TextUnformatted("LMB orbit RMB/MMB pan wheel zoom F frame");
|
||||
ImGui::TextUnformatted("LMB orbit Shift+LMB/RMB/MMB pan wheel zoom F frame");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
float ViewerApp::sceneRadiusFromOrigin() const {
|
||||
const Range3D bounds = _sceneData.initialBounds;
|
||||
float radius = 0.5f;
|
||||
for(const Vector3& corner: {
|
||||
Vector3{bounds.min().x(), bounds.min().y(), bounds.min().z()},
|
||||
Vector3{bounds.min().x(), bounds.min().y(), bounds.max().z()},
|
||||
Vector3{bounds.min().x(), bounds.max().y(), bounds.min().z()},
|
||||
Vector3{bounds.min().x(), bounds.max().y(), bounds.max().z()},
|
||||
Vector3{bounds.max().x(), bounds.min().y(), bounds.min().z()},
|
||||
Vector3{bounds.max().x(), bounds.min().y(), bounds.max().z()},
|
||||
Vector3{bounds.max().x(), bounds.max().y(), bounds.min().z()},
|
||||
Vector3{bounds.max().x(), bounds.max().y(), bounds.max().z()},
|
||||
}) {
|
||||
radius = Math::max(radius, corner.length());
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
Vector3 ViewerApp::sceneCenter() const {
|
||||
const Range3D bounds = _sceneData.initialBounds;
|
||||
return (bounds.min() + bounds.max())*0.5f;
|
||||
}
|
||||
|
||||
float ViewerApp::sceneRadius() const {
|
||||
const Range3D bounds = _sceneData.initialBounds;
|
||||
return Math::max((bounds.max() - bounds.min()).length()*0.5f, 0.5f);
|
||||
}
|
||||
|
||||
void ViewerApp::seekPlayback(float time) {
|
||||
if(_sceneData.times.empty()) {
|
||||
_currentTime = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
_currentTime = Math::clamp(time, _sceneData.times.front(), _sceneData.times.back());
|
||||
updateSceneAtCurrentTime();
|
||||
}
|
||||
|
||||
void ViewerApp::resetDefaultCamera() {
|
||||
_orbit.setSceneBounds({}, sceneRadiusFromOrigin());
|
||||
_cameraObject.setTransformation(_orbit.cameraTransform());
|
||||
}
|
||||
|
||||
void ViewerApp::resetCameraToScene() {
|
||||
_orbit.setSceneBounds(sceneCenter(), sceneRadius());
|
||||
_cameraObject.setTransformation(_orbit.cameraTransform());
|
||||
}
|
||||
|
||||
void ViewerApp::rewindPlayback() {
|
||||
seekPlayback(_sceneData.times.empty() ? 0.0f : _sceneData.times.front());
|
||||
_isPlaying = false;
|
||||
}
|
||||
|
||||
void ViewerApp::drawEvent() {
|
||||
updatePlayback();
|
||||
updateSceneAtCurrentTime();
|
||||
@@ -292,19 +479,16 @@ void ViewerApp::keyPressEvent(KeyEvent& event) {
|
||||
|
||||
switch(event.key()) {
|
||||
case Key::Space:
|
||||
if(!_isPlaying && !_sceneData.times.empty() && _currentTime >= _sceneData.times.back()) _currentTime = 0.0f;
|
||||
if(!_isPlaying && !_sceneData.times.empty() && _currentTime >= _sceneData.times.back()) rewindPlayback();
|
||||
_isPlaying = !_isPlaying;
|
||||
event.setAccepted();
|
||||
return;
|
||||
case Key::R:
|
||||
_currentTime = 0.0f;
|
||||
_isPlaying = false;
|
||||
rewindPlayback();
|
||||
event.setAccepted();
|
||||
return;
|
||||
case Key::F: {
|
||||
const Vector3 center = (_sceneData.initialBounds.min() + _sceneData.initialBounds.max())*0.5f;
|
||||
const float radius = Math::max((_sceneData.initialBounds.max() - _sceneData.initialBounds.min()).max(), 0.5f);
|
||||
_orbit.setSceneBounds(center, radius);
|
||||
resetCameraToScene();
|
||||
event.setAccepted();
|
||||
return;
|
||||
}
|
||||
@@ -328,7 +512,9 @@ void ViewerApp::keyReleaseEvent(KeyEvent& event) {
|
||||
void ViewerApp::pointerPressEvent(PointerEvent& event) {
|
||||
if(_imgui.handlePointerPressEvent(event)) return;
|
||||
_lastPointerPosition = event.position();
|
||||
if(event.pointer() == Pointer::MouseLeft) _dragMode = DragMode::Orbit;
|
||||
if(event.pointer() == Pointer::MouseLeft) {
|
||||
_dragMode = (event.modifiers() & Modifier::Shift) ? DragMode::Pan : DragMode::Orbit;
|
||||
}
|
||||
else if(event.pointer() == Pointer::MouseRight || event.pointer() == Pointer::MouseMiddle) _dragMode = DragMode::Pan;
|
||||
event.setAccepted();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <Magnum/Math/Color.h>
|
||||
#include <Magnum/Math/Matrix4.h>
|
||||
#include <Magnum/Math/Time.h>
|
||||
#include <Magnum/Shaders/FlatGL.h>
|
||||
#include <Magnum/SceneGraph/Camera.h>
|
||||
#include <Magnum/SceneGraph/Drawable.h>
|
||||
#include <Magnum/SceneGraph/MatrixTransformation3D.h>
|
||||
@@ -38,6 +39,7 @@ struct CliOptions {
|
||||
std::vector<std::string> geometryDirs;
|
||||
float initialSpeed = 1.0f;
|
||||
bool startPaused = false;
|
||||
bool reverseMouseY = false;
|
||||
};
|
||||
|
||||
class ViewerApp: public Magnum::Platform::Application {
|
||||
@@ -99,12 +101,36 @@ private:
|
||||
Magnum::Color4 _color;
|
||||
};
|
||||
|
||||
class FlatDrawable final: public Drawable3D {
|
||||
public:
|
||||
FlatDrawable(Object3D& object,
|
||||
Magnum::Shaders::FlatGL3D& shader,
|
||||
Magnum::GL::Mesh& mesh,
|
||||
Magnum::Color4 color,
|
||||
DrawableGroup3D& drawables);
|
||||
|
||||
private:
|
||||
void draw(const Magnum::Matrix4& transformation, Camera3D& camera) override;
|
||||
|
||||
Magnum::Shaders::FlatGL3D& _shader;
|
||||
Magnum::GL::Mesh& _mesh;
|
||||
Magnum::Color4 _color;
|
||||
};
|
||||
|
||||
std::shared_ptr<GpuMesh> uploadMesh(const CpuMesh& mesh) const;
|
||||
void createSceneObjects();
|
||||
void createReferenceObjects();
|
||||
void updatePlayback();
|
||||
void updateSceneAtCurrentTime();
|
||||
void drawUi();
|
||||
std::size_t sampleIndexForTime(float time) const;
|
||||
[[nodiscard]] float sceneRadiusFromOrigin() const;
|
||||
[[nodiscard]] Magnum::Vector3 sceneCenter() const;
|
||||
[[nodiscard]] float sceneRadius() const;
|
||||
void seekPlayback(float time);
|
||||
void resetDefaultCamera();
|
||||
void resetCameraToScene();
|
||||
void rewindPlayback();
|
||||
|
||||
CliOptions _options;
|
||||
LoadedScene _sceneData;
|
||||
@@ -118,6 +144,7 @@ private:
|
||||
DrawableGroup3D _drawables;
|
||||
|
||||
Magnum::Shaders::PhongGL _shader;
|
||||
Magnum::Shaders::FlatGL3D _flatShader;
|
||||
std::vector<std::shared_ptr<GpuMesh>> _gpuMeshes;
|
||||
std::vector<std::unique_ptr<Drawable3D>> _drawableStorage;
|
||||
std::vector<std::unique_ptr<Object3D>> _objectStorage;
|
||||
@@ -125,8 +152,12 @@ private:
|
||||
std::vector<SceneMarker> _markers;
|
||||
|
||||
std::shared_ptr<GpuMesh> _markerMesh;
|
||||
std::shared_ptr<GpuMesh> _boxMesh;
|
||||
Magnum::GL::Mesh _groundGridMesh;
|
||||
Magnum::GL::Mesh _originWireBoxMesh;
|
||||
|
||||
bool _isPlaying = false;
|
||||
bool _loopPlayback = true;
|
||||
float _playbackSpeed = 1.0f;
|
||||
float _currentTime = 0.0f;
|
||||
std::chrono::steady_clock::time_point _lastTick;
|
||||
|
||||
+1
-1
@@ -13,10 +13,10 @@ int main(int argc, char** argv) {
|
||||
cli.add_option("--geometry-dir", options.geometryDirs, "Additional geometry search directories");
|
||||
cli.add_option("--speed", options.initialSpeed, "Initial playback speed")->check(CLI::PositiveNumber);
|
||||
cli.add_flag("--start-paused", options.startPaused, "Start paused at t=0");
|
||||
cli.add_flag("--reverse-mouse-y", options.reverseMouseY, "Reverse vertical orbit drag direction");
|
||||
|
||||
CLI11_PARSE(cli, argc, argv);
|
||||
|
||||
osim_viewer::ViewerApp app({argc, argv}, std::move(options));
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user