remove camutils

This commit is contained in:
Nick Fisher
2025-04-02 22:19:42 +08:00
parent e8aa68beb4
commit 9d4fd4d6d4
5 changed files with 0 additions and 1095 deletions

View File

@@ -1,98 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <camutils/Bookmark.h>
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#include <math/vec3.h>
using namespace filament::math;
namespace filament {
namespace camutils {
template <typename FLOAT>
Bookmark<FLOAT> Bookmark<FLOAT>::interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t) {
Bookmark<FLOAT> result;
using float3 = filament::math::vec3<FLOAT>;
if (a.mode == Mode::MAP) {
assert(b.mode == Mode::MAP);
const double rho = sqrt(2.0);
const double rho2 = 2, rho4 = 4;
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
const double dr = r1 - r0;
const int valid = !std::isnan(dr) && dr != 0;
const double S = (valid ? dr : log(w1 / w0)) / rho;
const double s = t * S;
// This performs Van Wijk interpolation to animate between two waypoints on a map.
if (valid) {
const double coshr0 = cosh(r0);
const double u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
Bookmark<FLOAT> result;
result.map.center.x = ux0 + u * dx;
result.map.center.y = uy0 + u * dy;
result.map.extent = w0 * coshr0 / cosh(rho * s + r0);
return result;
}
// For degenerate cases, fall back to a simplified interpolation method.
result.map.center.x = ux0 + t * dx;
result.map.center.y = uy0 + t * dy;
result.map.extent = w0 * exp(rho * s);
return result;
}
assert(b.mode == Mode::ORBIT);
result.orbit.phi = lerp(a.orbit.phi, b.orbit.phi, FLOAT(t));
result.orbit.theta = lerp(a.orbit.theta, b.orbit.theta, FLOAT(t));
result.orbit.distance = lerp(a.orbit.distance, b.orbit.distance, FLOAT(t));
result.orbit.pivot = lerp(a.orbit.pivot, b.orbit.pivot, float3(t));
return result;
}
// Uses the Van Wijk method to suggest a duration for animating between two waypoints on a map.
// This does not have units, so just use it as a multiplier.
template <typename FLOAT>
double Bookmark<FLOAT>::duration(Bookmark<FLOAT> a, Bookmark<FLOAT> b) {
assert(a.mode == Mode::ORBIT && b.mode == Mode::ORBIT);
const double rho = sqrt(2.0);
const double rho2 = 2, rho4 = 4;
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
const double dr = r1 - r0;
const int valid = !std::isnan(dr) && dr != 0;
const double S = (valid ? dr : log(w1 / w0)) / rho;
return fabs(S);
}
template class Bookmark<float>;
} // namespace camutils
} // namespace filament

View File

@@ -1,206 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_FREEFLIGHT_MANIPULATOR_H
#define CAMUTILS_FREEFLIGHT_MANIPULATOR_H
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/quat.h>
#include <cmath>
namespace filament {
namespace camutils {
using namespace filament::math;
template<typename FLOAT>
class FreeFlightManipulator : public Manipulator<FLOAT> {
public:
using vec2 = filament::math::vec2<FLOAT>;
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
using Bookmark = filament::camutils::Bookmark<FLOAT>;
using Base = Manipulator<FLOAT>;
using Config = typename Base::Config;
FreeFlightManipulator(Mode mode, const Config& props) : Base(mode, props) {
setProperties(props);
Base::mEye = Base::mProps.flightStartPosition;
const auto pitch = Base::mProps.flightStartPitch;
const auto yaw = Base::mProps.flightStartYaw;
mTargetEuler = {pitch, yaw};
updateTarget(pitch, yaw);
}
void setProperties(const Config& props) override {
Config resolved = props;
if (resolved.flightPanSpeed == vec2(0, 0)) {
resolved.flightPanSpeed = vec2(0.01, 0.01);
}
if (resolved.flightMaxSpeed == 0.0) {
resolved.flightMaxSpeed = 10.0;
}
if (resolved.flightSpeedSteps == 0) {
resolved.flightSpeedSteps = 80;
}
Base::setProperties(resolved);
}
void updateTarget(FLOAT pitch, FLOAT yaw) {
Base::mTarget = Base::mEye + (mat3::eulerZYX(0, yaw, pitch) * vec3(0.0, 0.0, -1.0));
}
void grabBegin(int x, int y, bool strafe) override {
mGrabWin = {x, y};
mGrabbing = true;
mGrabEuler = mTargetEuler;
}
void grabUpdate(int x, int y) override {
if (!mGrabbing) {
return;
}
const vec2 del = mGrabWin - vec2{x, y};
const auto& grabPitch = mGrabEuler.x;
const auto& grabYaw = mGrabEuler.y;
auto& pitch = mTargetEuler.x;
auto& yaw = mTargetEuler.y;
constexpr double EPSILON = 0.001;
auto panSpeed = Base::mProps.flightPanSpeed;
constexpr FLOAT minPitch = (-F_PI_2 + EPSILON);
constexpr FLOAT maxPitch = ( F_PI_2 - EPSILON);
pitch = clamp(grabPitch + del.y * -panSpeed.y, minPitch, maxPitch);
yaw = fmod(grabYaw + del.x * panSpeed.x, 2.0 * F_PI);
updateTarget(pitch, yaw);
}
void grabEnd() override {
mGrabbing = false;
}
void keyDown(typename Base::Key key) override {
mKeyDown[(int) key] = true;
}
void keyUp(typename Base::Key key) override {
mKeyDown[(int) key] = false;
}
void scroll(int x, int y, FLOAT scrolldelta) override {
const FLOAT halfSpeedSteps = Base::mProps.flightSpeedSteps / 2;
mScrollWheel = clamp(mScrollWheel + scrolldelta, -halfSpeedSteps, halfSpeedSteps);
// Normalize the scroll position from -1 to 1 and calculate the move speed, in world
// units per second.
mScrollPositionNormalized = (mScrollWheel + halfSpeedSteps) / halfSpeedSteps - 1.0;
mMoveSpeed = pow(Base::mProps.flightMaxSpeed, mScrollPositionNormalized);
}
void update(FLOAT deltaTime) override {
vec3 forceLocal { 0.0, 0.0, 0.0 };
if (mKeyDown[(int) Base::Key::FORWARD]) {
forceLocal += vec3{ 0.0, 0.0, -1.0 };
}
if (mKeyDown[(int) Base::Key::LEFT]) {
forceLocal += vec3{ -1.0, 0.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::BACKWARD]) {
forceLocal += vec3{ 0.0, 0.0, 1.0 };
}
if (mKeyDown[(int) Base::Key::RIGHT]) {
forceLocal += vec3{ 1.0, 0.0, 0.0 };
}
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz;
if (mKeyDown[(int) Base::Key::UP]) {
forceWorld += vec3{ 0.0, 1.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::DOWN]) {
forceWorld += vec3{ 0.0, -1.0, 0.0 };
}
forceWorld *= mMoveSpeed;
const auto dampingFactor = Base::mProps.flightMoveDamping;
if (dampingFactor == 0.0) {
// Without damping, we simply treat the force as our velocity.
mEyeVelocity = forceWorld;
} else {
// The dampingFactor acts as "friction", which acts upon the camera in the direction
// opposite its velocity.
// Force is also multiplied by the dampingFactor, to "make up" for the friction.
// This ensures that the max velocity still approaches mMoveSpeed;
vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor;
mEyeVelocity += velocityDelta * deltaTime;
}
const vec3 positionDelta = mEyeVelocity * deltaTime;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
}
Bookmark getCurrentBookmark() const override {
Bookmark bookmark;
bookmark.flight.position = Base::mEye;
bookmark.flight.pitch = mTargetEuler.x;
bookmark.flight.yaw = mTargetEuler.y;
return bookmark;
}
Bookmark getHomeBookmark() const override {
Bookmark bookmark;
bookmark.flight.position = Base::mProps.flightStartPosition;
bookmark.flight.pitch = Base::mProps.flightStartPitch;
bookmark.flight.yaw = Base::mProps.flightStartYaw;
return bookmark;
}
void jumpToBookmark(const Bookmark& bookmark) override {
Base::mEye = bookmark.flight.position;
updateTarget(bookmark.flight.pitch, bookmark.flight.yaw);
}
private:
vec2 mGrabWin;
vec2 mTargetEuler; // (pitch, yaw)
vec2 mGrabEuler; // (pitch, yaw)
bool mKeyDown[(int) Base::Key::COUNT] = {false};
bool mGrabbing = false;
FLOAT mScrollWheel = 0.0f;
FLOAT mScrollPositionNormalized = 0.0f;
FLOAT mMoveSpeed = 1.0f;
vec3 mEyeVelocity;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_FREEFLIGHT_MANIPULATOR_H */

View File

@@ -1,372 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <math/scalar.h>
#include <camutils/Manipulator.h>
#include "FreeFlightManipulator.h"
#include "MapManipulator.h"
#include "OrbitManipulator.h"
using namespace filament::math;
namespace filament
{
namespace camutils
{
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::viewport(int width, int height)
{
details.viewport[0] = width;
details.viewport[1] = height;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z)
{
details.targetPosition = {x, y, z};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::upVector(FLOAT x, FLOAT y, FLOAT z)
{
details.upVector = {x, y, z};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::zoomSpeed(FLOAT val)
{
details.zoomSpeed = val;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z)
{
details.orbitHomePosition = {x, y, z};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::orbitSpeed(FLOAT x, FLOAT y)
{
details.orbitSpeed = {x, y};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::fovDirection(Fov fov)
{
details.fovDirection = fov;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::fovDegrees(FLOAT degrees)
{
details.fovDegrees = degrees;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::farPlane(FLOAT distance)
{
details.farPlane = distance;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight)
{
details.mapExtent = {worldWidth, worldHeight};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::mapMinDistance(FLOAT mindist)
{
details.mapMinDistance = mindist;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::flightStartPosition(FLOAT x, FLOAT y, FLOAT z)
{
details.flightStartPosition = {x, y, z};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw)
{
details.flightStartPitch = pitch;
details.flightStartYaw = yaw;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::flightMaxMoveSpeed(FLOAT maxSpeed)
{
details.flightMaxSpeed = maxSpeed;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::flightSpeedSteps(int steps)
{
details.flightSpeedSteps = steps;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::flightPanSpeed(FLOAT x, FLOAT y)
{
details.flightPanSpeed = {x, y};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::flightMoveDamping(FLOAT damping)
{
details.flightMoveDamping = damping;
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d)
{
details.groundPlane = {a, b, c, d};
return *this;
}
template <typename FLOAT>
typename Manipulator<FLOAT>::Builder &Manipulator<FLOAT>::Builder::raycastCallback(RayCallback cb, void *userdata)
{
details.raycastCallback = cb;
details.raycastUserdata = userdata;
return *this;
}
template <typename FLOAT>
Manipulator<FLOAT> *Manipulator<FLOAT>::Builder::build(Mode mode)
{
switch (mode)
{
case Mode::FREE_FLIGHT:
return new FreeFlightManipulator<FLOAT>(mode, details);
case Mode::MAP:
return new MapManipulator<FLOAT>(mode, details);
case Mode::ORBIT:
return new OrbitManipulator<FLOAT>(mode, details);
}
// just to make MSVC happy
return new OrbitManipulator<FLOAT>(mode, details);
}
template <typename FLOAT>
Manipulator<FLOAT>::Manipulator(Mode mode, const Config &props) : mMode(mode)
{
setProperties(props);
}
template <typename FLOAT>
void Manipulator<FLOAT>::setProperties(const Config &props)
{
mProps = props;
if (mProps.zoomSpeed == FLOAT(0))
{
mProps.zoomSpeed = 0.01;
}
if (mProps.upVector == vec3(0))
{
mProps.upVector = vec3(0, 1, 0);
}
if (mProps.fovDegrees == FLOAT(0))
{
mProps.fovDegrees = 33;
}
if (mProps.farPlane == FLOAT(0))
{
mProps.farPlane = 5000;
}
if (mProps.mapExtent == vec2(0))
{
mProps.mapExtent = vec2(512);
}
}
template <typename FLOAT>
void Manipulator<FLOAT>::setViewport(int width, int height)
{
Config props = mProps;
props.viewport[0] = width;
props.viewport[1] = height;
setProperties(props);
}
template <typename FLOAT>
void Manipulator<FLOAT>::getLookAt(vec3 *eyePosition, vec3 *targetPosition, vec3 *upward) const
{
*targetPosition = mTarget;
*eyePosition = mEye;
const vec3 gaze = normalize(mTarget - mEye);
const vec3 right = cross(gaze, mProps.upVector);
*upward = cross(right, gaze);
}
template <typename FLOAT>
static bool raycastPlane(const filament::math::vec3<FLOAT> &origin,
const filament::math::vec3<FLOAT> &dir, FLOAT *t, void *userdata)
{
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
auto props = (const typename Manipulator<FLOAT>::Config *)userdata;
const vec4 plane = props->groundPlane;
const vec3 n = vec3(plane[0], plane[1], plane[2]);
const vec3 p0 = n * plane[3];
const FLOAT denom = -dot(n, dir);
if (denom > 1e-6)
{
const vec3 p0l0 = p0 - origin;
*t = dot(p0l0, n) / -denom;
return *t >= 0;
}
return false;
}
template <typename FLOAT>
void Manipulator<FLOAT>::getRay(int x, int y, vec3 *porigin, vec3 *pdir) const
{
const vec3 gaze = normalize(mTarget - mEye);
const vec3 right = normalize(cross(gaze, mProps.upVector));
const vec3 upward = cross(right, gaze);
const FLOAT width = mProps.viewport[0];
const FLOAT height = mProps.viewport[1];
const FLOAT fov = mProps.fovDegrees * F_PI / 180.0;
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
const FLOAT tangent = tan(fov / 2.0);
const FLOAT aspect = width / height;
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
vec3 dir = gaze;
if (mProps.fovDirection == Fov::VERTICAL)
{
dir += right * tangent * u * aspect;
dir += upward * tangent * v;
}
else
{
dir += right * tangent * u;
dir += upward * tangent * v / aspect;
}
dir = normalize(dir);
*porigin = mEye;
*pdir = dir;
}
template <typename FLOAT>
bool Manipulator<FLOAT>::raycast(int x, int y, vec3 *result) const
{
vec3 origin, dir;
getRay(x, y, &origin, &dir);
// Choose either the user's callback function or the plane intersector.
auto callback = mProps.raycastCallback;
auto fallback = raycastPlane<FLOAT>;
void *userdata = mProps.raycastUserdata;
if (!callback)
{
callback = fallback;
userdata = (void *)&mProps;
}
// If the ray misses, then try the fallback function.
FLOAT t;
if (!callback(mEye, dir, &t, userdata))
{
if (callback == fallback || !fallback(mEye, dir, &t, (void *)&mProps))
{
return false;
}
}
*result = mEye + dir * t;
return true;
}
template <typename FLOAT>
filament::math::vec3<FLOAT> Manipulator<FLOAT>::raycastFarPlane(int x, int y) const
{
const filament::math::vec3<FLOAT> gaze = normalize(mTarget - mEye);
const vec3 right = cross(gaze, mProps.upVector);
const vec3 upward = cross(right, gaze);
const FLOAT width = mProps.viewport[0];
const FLOAT height = mProps.viewport[1];
const FLOAT fov = mProps.fovDegrees * math::F_PI / 180.0;
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
const FLOAT tangent = tan(fov / 2.0);
const FLOAT aspect = width / height;
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
vec3 dir = gaze;
if (mProps.fovDirection == Fov::VERTICAL)
{
dir += right * tangent * u * aspect;
dir += upward * tangent * v;
}
else
{
dir += right * tangent * u;
dir += upward * tangent * v / aspect;
}
return mEye + dir * mProps.farPlane;
}
template <typename FLOAT>
void Manipulator<FLOAT>::keyDown(Manipulator<FLOAT>::Key key) {}
template <typename FLOAT>
void Manipulator<FLOAT>::keyUp(Manipulator<FLOAT>::Key key) {}
template <typename FLOAT>
void Manipulator<FLOAT>::update(FLOAT deltaTime) {}
template class Manipulator<float>;
template class Manipulator<double>;
} // namespace camutils
} // namespace filament

View File

@@ -1,218 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_MAP_MANIPULATOR_H
#define CAMUTILS_MAP_MANIPULATOR_H
#include <math/scalar.h>
#include <camutils/Manipulator.h>
#include <math/vec3.h>
namespace filament
{
namespace camutils
{
template <typename FLOAT>
class MapManipulator : public Manipulator<FLOAT>
{
public:
using vec2 = math::vec2<FLOAT>;
using vec3 = math::vec3<FLOAT>;
using vec4 = math::vec4<FLOAT>;
using Bookmark = filament::camutils::Bookmark<FLOAT>;
using Base = Manipulator<FLOAT>;
using Config = typename Manipulator<FLOAT>::Config;
MapManipulator(Mode mode, const Config &props) : Manipulator<FLOAT>(mode, props)
{
const FLOAT width = Base::mProps.mapExtent.x;
const FLOAT height = Base::mProps.mapExtent.y;
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const FLOAT halfExtent = (horiz ? width : height) / 2.0;
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT distance = halfExtent / tan(fov / 2.0);
Base::mTarget = Base::mProps.targetPosition;
Base::mEye = Base::mTarget + distance * targetToEye;
}
void grabBegin(int x, int y, bool strafe) override
{
if (strafe || !Base::raycast(x, y, &mGrabScene))
{
return;
}
mGrabFar = Base::raycastFarPlane(x, y);
mGrabEye = Base::mEye;
mGrabTarget = Base::mTarget;
mGrabbing = true;
}
void grabUpdate(int x, int y) override
{
if (mGrabbing)
{
const FLOAT ulen = distance(mGrabScene, mGrabEye);
const FLOAT vlen = distance(mGrabFar, mGrabScene);
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
const vec3 eyePosition = mGrabEye + translation;
const vec3 targetPosition = mGrabTarget + translation;
moveWithConstraints(eyePosition, targetPosition);
}
}
void grabEnd() override
{
mGrabbing = false;
}
void scroll(int x, int y, FLOAT scrolldelta) override
{
vec3 grabScene;
if (!Base::raycast(x, y, &grabScene))
{
return;
}
// Find the direction of travel for the dolly. We do not normalize since it
// is desirable to move faster when further away from the targetPosition.
vec3 u = grabScene - Base::mEye;
// Prevent getting stuck when zooming in.
if (scrolldelta < 0)
{
const FLOAT distanceToSurface = length(u);
if (distanceToSurface < Base::mProps.zoomSpeed)
{
return;
}
}
u *= -scrolldelta * Base::mProps.zoomSpeed;
const vec3 eyePosition = Base::mEye + u;
const vec3 targetPosition = Base::mTarget + u;
moveWithConstraints(eyePosition, targetPosition);
}
Bookmark getCurrentBookmark() const override
{
const vec3 dir = normalize(Base::mTarget - Base::mEye);
FLOAT distance;
raycastPlane(Base::mEye, dir, &distance);
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT halfExtent = distance * tan(fov / 2.0);
vec3 targetPosition = Base::mEye + dir * distance;
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
const vec3 vvec = cross(targetToEye, uvec);
const vec3 centerToTarget = targetPosition - Base::mProps.targetPosition;
Bookmark bookmark;
bookmark.mode = Mode::MAP;
bookmark.map.extent = halfExtent * 2.0;
bookmark.map.center.x = dot(uvec, centerToTarget);
bookmark.map.center.y = dot(vvec, centerToTarget);
bookmark.orbit.theta = 0;
bookmark.orbit.phi = 0;
bookmark.orbit.pivot = Base::mProps.targetPosition +
uvec * bookmark.map.center.x +
vvec * bookmark.map.center.y;
bookmark.orbit.distance = halfExtent / tan(fov / 2.0);
return bookmark;
}
Bookmark getHomeBookmark() const override
{
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT width = Base::mProps.mapExtent.x;
const FLOAT height = Base::mProps.mapExtent.y;
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
Bookmark bookmark;
bookmark.mode = Mode::MAP;
bookmark.map.extent = horiz ? width : height;
bookmark.map.center.x = 0;
bookmark.map.center.y = 0;
bookmark.orbit.theta = 0;
bookmark.orbit.phi = 0;
bookmark.orbit.pivot = Base::mTarget;
bookmark.orbit.distance = 0.5 * bookmark.map.extent / tan(fov / 2.0);
// TODO: Add optional boundary constraints here.
return bookmark;
}
void jumpToBookmark(const Bookmark &bookmark) override
{
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const FLOAT halfExtent = bookmark.map.extent / 2.0;
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT distance = halfExtent / tan(fov / 2.0);
vec3 uvec = cross(Base::mProps.upVector, targetToEye);
vec3 vvec = cross(targetToEye, uvec);
uvec = normalize(uvec) * bookmark.map.center.x;
vvec = normalize(vvec) * bookmark.map.center.y;
Base::mTarget = Base::mProps.targetPosition + uvec + vvec;
Base::mEye = Base::mTarget + distance * targetToEye;
}
private:
bool raycastPlane(const vec3 &origin, const vec3 &dir, FLOAT *t) const
{
const vec4 plane = Base::mProps.groundPlane;
const vec3 n = vec3(plane[0], plane[1], plane[2]);
const vec3 p0 = n * plane[3];
const FLOAT denom = -dot(n, dir);
if (denom > 1e-6)
{
const vec3 p0l0 = p0 - origin;
*t = dot(p0l0, n) / -denom;
return *t >= 0;
}
return false;
}
void moveWithConstraints(vec3 eye, vec3 targetPosition)
{
Base::mEye = eye;
Base::mTarget = targetPosition;
// TODO: Add optional boundary constraints here.
}
private:
bool mGrabbing = false;
vec3 mGrabScene;
vec3 mGrabFar;
vec3 mGrabEye;
vec3 mGrabTarget;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_MAP_MANIPULATOR_H */

View File

@@ -1,201 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_ORBIT_MANIPULATOR_H
#define CAMUTILS_ORBIT_MANIPULATOR_H
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#define MAX_PHI (F_PI / 2.0 - 0.001)
namespace filament {
namespace camutils {
using namespace filament::math;
template<typename FLOAT>
class OrbitManipulator : public Manipulator<FLOAT> {
public:
using vec2 = filament::math::vec2<FLOAT>;
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
using Bookmark = filament::camutils::Bookmark<FLOAT>;
using Base = Manipulator<FLOAT>;
using Config = typename Base::Config;
enum GrabState { INACTIVE, ORBITING, PANNING };
OrbitManipulator(Mode mode, const Config& props) : Base(mode, props) {
setProperties(props);
Base::mEye = Base::mProps.orbitHomePosition;
mPivot = Base::mTarget = Base::mProps.targetPosition;
}
void setProperties(const Config& props) override {
Config resolved = props;
if (resolved.orbitHomePosition == vec3(0)) {
resolved.orbitHomePosition = vec3(0, 0, 1);
}
if (resolved.orbitSpeed == vec2(0)) {
resolved.orbitSpeed = vec2(0.01);
}
// By default, place the ground plane so that it aligns with the targetPosition position.
// This is used only when PANNING.
if (resolved.groundPlane == vec4(0)) {
const FLOAT d = length(resolved.targetPosition);
const vec3 n = normalize(resolved.orbitHomePosition - resolved.targetPosition);
resolved.groundPlane = vec4(n, -d);
}
Base::setProperties(resolved);
}
void grabBegin(int x, int y, bool strafe) override {
mGrabState = strafe ? PANNING : ORBITING;
mGrabPivot = mPivot;
mGrabEye = Base::mEye;
mGrabTarget = Base::mTarget;
mGrabBookmark = getCurrentBookmark();
mGrabWinX = x;
mGrabWinY = y;
mGrabFar = Base::raycastFarPlane(x, y);
Base::raycast(x, y, &mGrabScene);
}
void grabUpdate(int x, int y) override {
const int delx = mGrabWinX - x;
const int dely = mGrabWinY - y;
if (mGrabState == ORBITING) {
Bookmark bookmark = getCurrentBookmark();
const FLOAT theta = delx * Base::mProps.orbitSpeed.x;
const FLOAT phi = dely * Base::mProps.orbitSpeed.y;
const FLOAT maxPhi = MAX_PHI;
bookmark.orbit.phi = clamp(mGrabBookmark.orbit.phi + phi, -maxPhi, +maxPhi);
bookmark.orbit.theta = mGrabBookmark.orbit.theta + theta;
jumpToBookmark(bookmark);
}
if (mGrabState == PANNING) {
const FLOAT ulen = distance(mGrabScene, mGrabEye);
const FLOAT vlen = distance(mGrabFar, mGrabScene);
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
mPivot = mGrabPivot + translation;
Base::mEye = mGrabEye + translation;
Base::mTarget = mGrabTarget + translation;
}
}
void grabEnd() override {
mGrabState = INACTIVE;
}
void scroll(int x, int y, FLOAT scrolldelta) override {
const vec3 gaze = normalize(Base::mTarget - Base::mEye);
const vec3 movement = gaze * Base::mProps.zoomSpeed * -scrolldelta;
const vec3 v0 = mPivot - Base::mEye;
Base::mEye += movement;
Base::mTarget += movement;
const vec3 v1 = mPivot - Base::mEye;
// Check if the camera has moved past the point of interest.
if (dot(v0, v1) < 0) {
mFlipped = !mFlipped;
}
}
Bookmark getCurrentBookmark() const override {
Bookmark bookmark;
bookmark.mode = Mode::ORBIT;
const vec3 pivotToEye = Base::mEye - mPivot;
const FLOAT d = length(pivotToEye);
const FLOAT x = pivotToEye.x / d;
const FLOAT y = pivotToEye.y / d;
const FLOAT z = pivotToEye.z / d;
bookmark.orbit.phi = asin(y);
bookmark.orbit.theta = atan2(x, z);
bookmark.orbit.distance = mFlipped ? -d : d;
bookmark.orbit.pivot = mPivot;
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT halfExtent = d * tan(fov / 2.0);
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
const vec3 vvec = cross(targetToEye, uvec);
const vec3 centerToTarget = mPivot - Base::mProps.targetPosition;
bookmark.map.extent = halfExtent * 2;
bookmark.map.center.x = dot(uvec, centerToTarget);
bookmark.map.center.y = dot(vvec, centerToTarget);
return bookmark;
}
Bookmark getHomeBookmark() const override {
Bookmark bookmark;
bookmark.mode = Mode::ORBIT;
bookmark.orbit.phi = FLOAT(0);
bookmark.orbit.theta = FLOAT(0);
bookmark.orbit.pivot = Base::mProps.targetPosition;
bookmark.orbit.distance = distance(Base::mProps.targetPosition, Base::mProps.orbitHomePosition);
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT halfExtent = bookmark.orbit.distance * tan(fov / 2.0);
bookmark.map.extent = halfExtent * 2;
bookmark.map.center.x = 0;
bookmark.map.center.y = 0;
return bookmark;
}
void jumpToBookmark(const Bookmark& bookmark) override {
mPivot = bookmark.orbit.pivot;
const FLOAT x = sin(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
const FLOAT y = sin(bookmark.orbit.phi);
const FLOAT z = cos(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
Base::mEye = mPivot + vec3(x, y, z) * abs(bookmark.orbit.distance);
mFlipped = bookmark.orbit.distance < 0;
Base::mTarget = Base::mEye + vec3(x, y, z) * (mFlipped ? 1.0 : -1.0);
}
private:
GrabState mGrabState = INACTIVE;
bool mFlipped = false;
vec3 mGrabPivot;
vec3 mGrabScene;
vec3 mGrabFar;
vec3 mGrabEye;
vec3 mGrabTarget;
Bookmark mGrabBookmark;
int mGrabWinX;
int mGrabWinY;
vec3 mPivot;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_ORBIT_MANIPULATOR_H */