From 2854c0627dc63aad78fdb4620d2bfdff9d068b9f Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Tue, 14 Jan 2020 13:13:39 -0800 Subject: [PATCH] camutils: add support for Builder pattern. This makes the upcoming Java bindings simpler and improves parity with existing API conventions. Note that camutils does not depend on filament and therefore cannot use BuilderBase. Note that the Manipulator Builder state is public which breaks with convention but makes implementation simple and allows C++ clients to continue using modern initializer syntax if they wish. --- libs/camutils/include/camutils/Manipulator.h | 84 ++++++++++-------- libs/camutils/src/Manipulator.cpp | 89 +++++++++++++++++++- libs/camutils/tests/test_camutils.cpp | 28 +++--- samples/app/FilamentApp.cpp | 4 +- 4 files changed, 150 insertions(+), 55 deletions(-) diff --git a/libs/camutils/include/camutils/Manipulator.h b/libs/camutils/include/camutils/Manipulator.h index 648cfe2624..78bb146523 100644 --- a/libs/camutils/include/camutils/Manipulator.h +++ b/libs/camutils/include/camutils/Manipulator.h @@ -43,9 +43,9 @@ enum class Fov { VERTICAL, HORIZONTAL }; * CameraManipulator* manip; * * void init() { - * manip = CameraManipulator::create(camutils::Mode::ORBIT, { - * .viewport = {1024, 768} - * }); + * manip = CameraManipulator::Builder() + * .viewport(1024, 768) + * .build(camutils::Mode::ORBIT); * } * * void onMouseDown(int x, int y) { @@ -84,41 +84,55 @@ public: /** Optional raycasting function to enable perspective-correct panning. */ typedef bool (*RayCallback)(const vec3& origin, const vec3& dir, FLOAT* t, void* userdata); - /** - * User-controlled configuration that is never changed by the manipulator. - * - * This is a Builder expressed as a POD structure. Many of the fields fall back to reasonable - * default values if they are zero-filled. - */ + /** Builder state, direct access is allowed but Builder methods are preferred. **/ struct Config { - int viewport[2]; //! Width and height of the viewing area - vec3 targetPosition; //! World-space position of interest, defaults to (0,0,0) - vec3 upVector; //! Orientation for the home position, defaults to (0,1,0) - FLOAT zoomSpeed; //! Multiplied with scroll delta, defaults to 0.01 - - // Orbit mode properties - vec3 orbitHomePosition; //! Initial eye position in world space, defaults to (0,0,1) - vec2 orbitSpeed; //! Multiplied with viewport delta, defaults to 0.01 - - // Map mode properties - Fov fovDirection; //! The FOV axis that's held constant when the viewport changes - FLOAT fovDegrees; //! The full FOV (not the half-angle) - FLOAT farPlane; //! The distance to the far plane - vec2 mapExtent; //! The ground plane size used to compute the home position - FLOAT mapMinDistance; //! Constrains the zoom-in level - - // Raycast properties - vec4 groundPlane; //! Plane equation used as a raycast fallback - RayCallback raycastCallback; //! Raycast function for accurate grab-and-pan - void* raycastUserdata; //! Callback userdata for closures + int viewport[2]; + vec3 targetPosition; + vec3 upVector; + FLOAT zoomSpeed; + vec3 orbitHomePosition; + vec2 orbitSpeed; + Fov fovDirection; + FLOAT fovDegrees; + FLOAT farPlane; + vec2 mapExtent; + FLOAT mapMinDistance; + vec4 groundPlane; + RayCallback raycastCallback; + void* raycastUserdata; }; - /** - * Creates a new camera manipulator, either ORBIT or MAP. - * - * Clients can simply use "delete" to destroy the manipulator. - */ - static Manipulator* create(Mode mode, const Config& props); + struct Builder { + // Common properties + Builder& viewport(int width, int height); //! Width and height of the viewing area + Builder& targetPosition(FLOAT x, FLOAT y, FLOAT z); //! World-space position of interest, defaults to (0,0,0) + Builder& upVector(FLOAT x, FLOAT y, FLOAT z); //! Orientation for the home position, defaults to (0,1,0) + Builder& zoomSpeed(FLOAT val); //! Multiplied with scroll delta, defaults to 0.01 + + // Orbit mode properties + Builder& orbitHomePosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,1) + Builder& orbitSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01 + + // Map mode properties + Builder& fovDirection(Fov fov); //! The axis that's held constant when viewport changes + Builder& fovDegrees(FLOAT degrees); //! The full FOV (not the half-angle) + Builder& farPlane(FLOAT distance); //! The distance to the far plane + Builder& mapExtent(FLOAT worldWidth, FLOAT worldHeight); //! The ground size for computing home position + Builder& mapMinDistance(FLOAT mindist); //! Constrains the zoom-in level + + // Raycast properties + Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback + Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan + + /** + * Creates a new camera manipulator, either ORBIT or MAP. + * + * Clients can simply use "delete" to destroy the manipulator. + */ + Manipulator* build(Mode mode); + + Config details; + }; virtual ~Manipulator() = default; diff --git a/libs/camutils/src/Manipulator.cpp b/libs/camutils/src/Manipulator.cpp index 2326275012..1a62dde50c 100644 --- a/libs/camutils/src/Manipulator.cpp +++ b/libs/camutils/src/Manipulator.cpp @@ -26,12 +26,93 @@ using namespace filament::math; namespace filament { namespace camutils { -template Manipulator* -Manipulator::create(Mode mode, const Manipulator::Config& props) { +template typename +Manipulator::Builder& Manipulator::Builder::viewport(int width, int height) { + details.viewport[0] = width; + details.viewport[1] = height; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z) { + details.targetPosition = {x, y, z}; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::upVector(FLOAT x, FLOAT y, FLOAT z) { + details.upVector = {x, y, z}; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::zoomSpeed(FLOAT val) { + details.zoomSpeed = val; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z) { + details.orbitHomePosition = {x, y, z}; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::orbitSpeed(FLOAT x, FLOAT y) { + details.orbitSpeed = {x, y}; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::fovDirection(Fov fov) { + details.fovDirection = fov; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::fovDegrees(FLOAT degrees) { + details.fovDegrees = degrees; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::farPlane(FLOAT distance) { + details.farPlane = distance; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight) { + details.mapExtent = {worldWidth, worldHeight}; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::mapMinDistance(FLOAT mindist) { + details.mapMinDistance = mindist; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d) { + details.groundPlane = {a, b, c, d}; + return *this; +} + +template typename +Manipulator::Builder& Manipulator::Builder::raycastCallback(RayCallback cb, void* userdata) { + details.raycastCallback = cb; + details.raycastUserdata = userdata; + return *this; +} + + +template +Manipulator* Manipulator::Builder::build(Mode mode) { if (mode == Mode::MAP) { - return new MapManipulator(mode, props); + return new MapManipulator(mode, details); } - return new OrbitManipulator(mode, props); + return new OrbitManipulator(mode, details); } template diff --git a/libs/camutils/tests/test_camutils.cpp b/libs/camutils/tests/test_camutils.cpp index 92bf5dbb3a..ac2314d6c6 100644 --- a/libs/camutils/tests/test_camutils.cpp +++ b/libs/camutils/tests/test_camutils.cpp @@ -39,14 +39,14 @@ TEST_F(CamUtilsTest, Orbit) { float3 eye, targetPosition, up; - CamManipulator* orbit = CamManipulator::create(camutils::Mode::ORBIT, { - .viewport = {256, 256}, - .targetPosition = {0, 0, 0}, - .upVector = {0, 1, 0}, - .zoomSpeed = 0.01, - .orbitHomePosition = {0, 0, 4}, - .orbitSpeed = {1, 1}, - }); + CamManipulator* orbit = CamManipulator::Builder() + .viewport(256, 256) + .targetPosition(0, 0, 0) + .upVector(0, 1, 0) + .zoomSpeed(0.01) + .orbitHomePosition(0, 0, 4) + .orbitSpeed(1, 1) + .build(camutils::Mode::ORBIT); orbit->getLookAt(&eye, &targetPosition, &up); EXPECT_VEC_EQ(eye, 0, 0, 4); @@ -69,12 +69,12 @@ TEST_F(CamUtilsTest, Map) { float3 eye, targetPosition, up; - CamManipulator* map = CamManipulator::create(camutils::Mode::MAP, { - .viewport = {256, 256}, - .targetPosition = {0, 0, 0}, - .zoomSpeed = 0.01, - .orbitHomePosition = {0, 0, 4}, - }); + CamManipulator* map = CamManipulator::Builder() + .viewport(256, 256) + .targetPosition(0, 0, 0) + .zoomSpeed(0.01) + .orbitHomePosition(0, 0, 4) + .build(camutils::Mode::MAP); delete map; } diff --git a/samples/app/FilamentApp.cpp b/samples/app/FilamentApp.cpp index d4239fb3f2..e61a5c8858 100644 --- a/samples/app/FilamentApp.cpp +++ b/samples/app/FilamentApp.cpp @@ -522,8 +522,8 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, // set-up the camera manipulators camutils::Mode mode = camutils::Mode::ORBIT; - mMainCameraMan = CameraManipulator::create(mode, { .targetPosition = {0, 0, -4} }); - mDebugCameraMan = CameraManipulator::create(mode, { .targetPosition = {0, 0, -4} }); + mMainCameraMan = CameraManipulator::Builder().targetPosition(0, 0, -4).build(mode); + mDebugCameraMan = CameraManipulator::Builder().targetPosition(0, 0, -4).build(mode); mMainView->setCamera(mMainCamera); mMainView->setCameraManipulator(mMainCameraMan);