/* * 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 #include #include "FreeFlightManipulator.h" #include "MapManipulator.h" #include "OrbitManipulator.h" using namespace filament::math; namespace filament { namespace camutils { 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::flightStartPosition(FLOAT x, FLOAT y, FLOAT z) { details.flightStartPosition = {x, y, z}; return *this; } template typename Manipulator::Builder& Manipulator::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw) { details.flightStartPitch = pitch; details.flightStartYaw = yaw; return *this; } template typename Manipulator::Builder& Manipulator::Builder::flightMaxMoveSpeed(FLOAT maxSpeed) { details.flightMaxSpeed = maxSpeed; return *this; } template typename Manipulator::Builder& Manipulator::Builder::flightSpeedSteps(int steps) { details.flightSpeedSteps = steps; return *this; } template typename Manipulator::Builder& Manipulator::Builder::flightPanSpeed(FLOAT x, FLOAT y) { details.flightPanSpeed = {x, y}; return *this; } template typename Manipulator::Builder& Manipulator::Builder::flightMoveDamping(FLOAT damping) { details.flightMoveDamping = damping; 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) { switch (mode) { case Mode::FREE_FLIGHT: return new FreeFlightManipulator(mode, details); case Mode::MAP: return new MapManipulator(mode, details); case Mode::ORBIT: return new OrbitManipulator(mode, details); } } template Manipulator::Manipulator(Mode mode, const Config& props) : mMode(mode) { setProperties(props); } template void Manipulator::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 void Manipulator::setViewport(int width, int height) { Config props = mProps; props.viewport[0] = width; props.viewport[1] = height; setProperties(props); } template void Manipulator::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 static bool raycastPlane(const filament::math::vec3& origin, const filament::math::vec3& dir, FLOAT* t, void* userdata) { using vec3 = filament::math::vec3; using vec4 = filament::math::vec4; auto props = (const typename Manipulator::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 void Manipulator::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 bool Manipulator::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; 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 filament::math::vec3 Manipulator::raycastFarPlane(int x, int y) const { const filament::math::vec3 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 void Manipulator::keyDown(Manipulator::Key key) { } template void Manipulator::keyUp(Manipulator::Key key) { } template void Manipulator::update(FLOAT deltaTime) { } template class Manipulator; } // namespace camutils } // namespace filament