camera fixes for assets with large bounding boxes
This commit is contained in:
@@ -177,7 +177,7 @@ namespace polyvox {
|
||||
void loadPngTexture(string path, ResourceBuffer data);
|
||||
void loadTextureFromPath(string path);
|
||||
|
||||
|
||||
Manipulator<double>* _manipulator = nullptr;
|
||||
void _createManipulator();
|
||||
uint32_t _lastFrameTimeInNanos;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ A new flutter plugin project.
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/*', 'src/*', 'src/ios/*', 'include/filament/*', 'include/*', 'include/material/*.c'
|
||||
s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'src/ios/*', 'include/filament/*', 'include/*', 'include/material/*.c'
|
||||
s.public_header_files = 'include/SwiftPolyvoxFilamentPlugin-Bridging-Header.h', 'include/PolyvoxFilamentApi.h', 'include/PolyvoxFilamentFFIApi.h', 'include/ResourceBuffer.hpp', 'include/Log.hpp'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '12.1'
|
||||
@@ -26,7 +26,7 @@ A new flutter plugin project.
|
||||
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include/filament" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/shaders" "$(inherited)"',
|
||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
||||
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ A new flutter plugin project.
|
||||
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include/filament" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/shaders" "$(inherited)"',
|
||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
||||
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,32 +43,32 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->setBackgroundImage(path, fillHeight);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image_position(const void* const viewer, float x, float y, bool clamp) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image_position(const void* const viewer, float x, float y, bool clamp) {
|
||||
((FilamentViewer*)viewer)->setBackgroundImagePosition(x, y, clamp);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_tone_mapping(const void* const viewer, int toneMapping) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_tone_mapping(const void* const viewer, int toneMapping) {
|
||||
((FilamentViewer*)viewer)->setToneMapping((ToneMapping)toneMapping);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_bloom(const void* const viewer, float strength) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_bloom(const void* const viewer, float strength) {
|
||||
Log("Setting bloom to %f", strength);
|
||||
((FilamentViewer*)viewer)->setBloom(strength);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void load_skybox(const void* const viewer, const char* skyboxPath) {
|
||||
FLUTTER_PLUGIN_EXPORT void load_skybox(const void* const viewer, const char* skyboxPath) {
|
||||
((FilamentViewer*)viewer)->loadSkybox(skyboxPath);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void load_ibl(const void* const viewer, const char* iblPath, float intensity) {
|
||||
FLUTTER_PLUGIN_EXPORT void load_ibl(const void* const viewer, const char* iblPath, float intensity) {
|
||||
((FilamentViewer*)viewer)->loadIbl(iblPath, intensity);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->removeSkybox();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->removeIbl();
|
||||
}
|
||||
|
||||
@@ -76,11 +76,11 @@ extern "C" {
|
||||
return ((FilamentViewer*)viewer)->addLight((LightManager::Type)type, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_light(const void* const viewer, int32_t entityId) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_light(const void* const viewer, int32_t entityId) {
|
||||
((FilamentViewer*)viewer)->removeLight(entityId);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void clear_lights(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void clear_lights(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->clearLights();
|
||||
}
|
||||
|
||||
@@ -104,27 +104,27 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->moveCameraToAsset(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void* const viewer, float distance) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void* const viewer, float distance) {
|
||||
((FilamentViewer*)viewer)->setCameraFocusDistance(distance);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void* const viewer, float aperture, float shutterSpeed, float sensitivity) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void* const viewer, float aperture, float shutterSpeed, float sensitivity) {
|
||||
((FilamentViewer*)viewer)->setCameraExposure(aperture, shutterSpeed, sensitivity);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_position(const void* const viewer, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_position(const void* const viewer, float x, float y, float z) {
|
||||
((FilamentViewer*)viewer)->setCameraPosition(x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void* const viewer, float rads, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void* const viewer, float rads, float x, float y, float z) {
|
||||
((FilamentViewer*)viewer)->setCameraRotation(rads, x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void* const viewer, const float* const matrix) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void* const viewer, const float* const matrix) {
|
||||
((FilamentViewer*)viewer)->setCameraModelMatrix(matrix);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, float focalLength) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, float focalLength) {
|
||||
((FilamentViewer*)viewer)->setCameraFocalLength(focalLength);
|
||||
}
|
||||
|
||||
@@ -137,46 +137,46 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_frame_interval(
|
||||
FLUTTER_PLUGIN_EXPORT void set_frame_interval(
|
||||
const void* const viewer,
|
||||
float frameInterval
|
||||
) {
|
||||
((FilamentViewer*)viewer)->setFrameInterval(frameInterval);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->destroySwapChain();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const window, uint32_t width, uint32_t height) {
|
||||
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const window, uint32_t width, uint32_t height) {
|
||||
((FilamentViewer*)viewer)->createSwapChain(window, width, height);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(const void* const viewer, uint32_t width, uint32_t height, float scaleFactor) {
|
||||
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(const void* const viewer, uint32_t width, uint32_t height, float scaleFactor) {
|
||||
return ((FilamentViewer*)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_update(const void* const viewer, float x, float y, float delta) {
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_update(const void* const viewer, float x, float y, float delta) {
|
||||
((FilamentViewer*)viewer)->scrollUpdate(x, y, delta);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_begin(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_begin(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->scrollBegin();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_end(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_end(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->scrollEnd();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void grab_begin(const void* const viewer, float x, float y, bool pan) {
|
||||
FLUTTER_PLUGIN_EXPORT void grab_begin(const void* const viewer, float x, float y, bool pan) {
|
||||
((FilamentViewer*)viewer)->grabBegin(x, y, pan);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void grab_update(const void* const viewer, float x, float y) {
|
||||
FLUTTER_PLUGIN_EXPORT void grab_update(const void* const viewer, float x, float y) {
|
||||
((FilamentViewer*)viewer)->grabUpdate(x, y);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void grab_end(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void grab_end(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->grabEnd();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ extern "C" {
|
||||
return (void*)((FilamentViewer*)viewer)->getAssetManager();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void apply_weights(
|
||||
FLUTTER_PLUGIN_EXPORT void apply_weights(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
const char* const entityName,
|
||||
@@ -193,7 +193,7 @@ extern "C" {
|
||||
// ((AssetManager*)assetManager)->setMorphTargetWeights(asset, entityName, weights, count);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights(
|
||||
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
const char* const entityName,
|
||||
@@ -288,7 +288,7 @@ extern "C" {
|
||||
// }
|
||||
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void play_animation(
|
||||
FLUTTER_PLUGIN_EXPORT void play_animation(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
int index,
|
||||
@@ -299,7 +299,7 @@ extern "C" {
|
||||
((AssetManager*)assetManager)->playAnimation(asset, index, loop, reverse, replaceActive, crossfade);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_animation_frame(
|
||||
FLUTTER_PLUGIN_EXPORT void set_animation_frame(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
int animationIndex,
|
||||
@@ -319,7 +319,7 @@ extern "C" {
|
||||
return (int)names->size();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void get_animation_name(
|
||||
FLUTTER_PLUGIN_EXPORT void get_animation_name(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
char* const outPtr,
|
||||
@@ -335,17 +335,17 @@ extern "C" {
|
||||
return (int)names->size();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void* assetManager, EntityId asset, const char* meshName, char* const outPtr, int index ) {
|
||||
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void* assetManager, EntityId asset, const char* meshName, char* const outPtr, int index ) {
|
||||
unique_ptr<vector<string>> names = ((AssetManager*)assetManager)->getMorphTargetNames(asset, meshName);
|
||||
string name = names->at(index);
|
||||
strcpy(outPtr, name.c_str());
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_asset(const void* const viewer, EntityId asset) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_asset(const void* const viewer, EntityId asset) {
|
||||
((FilamentViewer*)viewer)->removeAsset(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void clear_assets(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void clear_assets(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->clearAssets();
|
||||
}
|
||||
|
||||
@@ -353,23 +353,23 @@ extern "C" {
|
||||
return ((AssetManager*)assetManager)->setMaterialColor(asset, meshName, materialIndex, r, g, b, a);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void* assetManager, EntityId asset) {
|
||||
FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void* assetManager, EntityId asset) {
|
||||
((AssetManager*)assetManager)->transformToUnitCube(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_position(void* assetManager, EntityId asset, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_position(void* assetManager, EntityId asset, float x, float y, float z) {
|
||||
((AssetManager*)assetManager)->setPosition(asset, x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) {
|
||||
((AssetManager*)assetManager)->setRotation(asset, rads, x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) {
|
||||
((AssetManager*)assetManager)->setScale(asset, scale);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void stop_animation(void* assetManager, EntityId asset, int index) {
|
||||
FLUTTER_PLUGIN_EXPORT void stop_animation(void* assetManager, EntityId asset, int index) {
|
||||
((AssetManager*)assetManager)->stopAnimation(asset, index);
|
||||
}
|
||||
|
||||
|
||||
98
ios/src/camutils/Bookmark.cpp
Normal file
98
ios/src/camutils/Bookmark.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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
|
||||
206
ios/src/camutils/FreeFlightManipulator.h
Normal file
206
ios/src/camutils/FreeFlightManipulator.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 */
|
||||
324
ios/src/camutils/Manipulator.cpp
Normal file
324
ios/src/camutils/Manipulator.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 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/Manipulator.h>
|
||||
|
||||
#include <math/scalar.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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
197
ios/src/camutils/MapManipulator.h
Normal file
197
ios/src/camutils/MapManipulator.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 <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 */
|
||||
201
ios/src/camutils/OrbitManipulator.h
Normal file
201
ios/src/camutils/OrbitManipulator.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -197,7 +197,7 @@ abstract class FilamentController {
|
||||
///
|
||||
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
||||
///
|
||||
Future zoomUpdate(double z);
|
||||
Future zoomUpdate(double x, double y, double z);
|
||||
|
||||
///
|
||||
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
||||
|
||||
@@ -191,16 +191,45 @@ class FilamentControllerFFI extends FilamentController {
|
||||
_isReadyForScene.complete(true);
|
||||
}
|
||||
|
||||
///
|
||||
/// I'm not exactly sure how to resize the backing textures on all platforms.
|
||||
/// So for now, I'm sticking with the safe option when the widget is resized: destroying the swapchain, recreating the textures, and creating a new swapchain.
|
||||
///
|
||||
@override
|
||||
Future resize(int width, int height, {double scaleFactor = 1.0}) async {
|
||||
_resizing = true;
|
||||
setRendering(false);
|
||||
_lib.destroy_swap_chain(_viewer!);
|
||||
await destroyTexture();
|
||||
size = ui.Size(width * _pixelRatio, height * _pixelRatio);
|
||||
_textureId = await _channel
|
||||
.invokeMethod("resize", [size.width, size.height, scaleFactor]);
|
||||
_textureIdController.add(_textureId);
|
||||
|
||||
var textures =
|
||||
await _channel.invokeMethod("createTexture", [size.width, size.height]);
|
||||
var flutterTextureId = textures[0];
|
||||
_textureId = flutterTextureId;
|
||||
|
||||
// void* on iOS (pointer to pixel buffer), void* on Android (pointer to native window), null on Windows/macOS
|
||||
var surfaceAddress = textures[1] as int? ?? 0;
|
||||
|
||||
// null on iOS/Android, void* on MacOS (pointer to metal texture), GLuid on Windows/Linux
|
||||
var nativeTexture = textures[2] as int? ?? 0;
|
||||
|
||||
_lib.create_swap_chain_ffi(
|
||||
_viewer!,
|
||||
Pointer<Void>.fromAddress(surfaceAddress),
|
||||
size.width.toInt(),
|
||||
size.height.toInt());
|
||||
if (nativeTexture != 0) {
|
||||
assert(surfaceAddress == 0);
|
||||
print("Creating render target from native texture $nativeTexture");
|
||||
_lib.create_render_target_ffi(
|
||||
_viewer!, nativeTexture, size.width.toInt(), size.height.toInt());
|
||||
}
|
||||
|
||||
_lib.update_viewport_and_camera_projection_ffi(
|
||||
_viewer!, size.width.toInt(), size.height.toInt(), scaleFactor);
|
||||
_viewer!, size.width.toInt(), size.height.toInt(), 1.0);
|
||||
|
||||
_textureIdController.add(_textureId);
|
||||
_resizing = false;
|
||||
setRendering(true);
|
||||
}
|
||||
@@ -553,11 +582,11 @@ class FilamentControllerFFI extends FilamentController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future zoomUpdate(double z) async {
|
||||
Future zoomUpdate(double x, double y, double z) async {
|
||||
if (_viewer == null || _resizing) {
|
||||
throw Exception("No viewer available, ignoring");
|
||||
}
|
||||
_lib.scroll_update(_viewer!, 0.0, 0.0, z);
|
||||
_lib.scroll_update(_viewer!, x, y, z);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -434,11 +434,11 @@ class FilamentControllerMethodChannel extends FilamentController {
|
||||
await _channel.invokeMethod("scrollBegin");
|
||||
}
|
||||
|
||||
Future zoomUpdate(double z) async {
|
||||
Future zoomUpdate(double x, double y, double z) async {
|
||||
if (_viewer == null || _resizing) {
|
||||
throw Exception("No viewer available, ignoring");
|
||||
}
|
||||
await _channel.invokeMethod("scrollUpdate", [0.0, 0.0, z]);
|
||||
await _channel.invokeMethod("scrollUpdate", [x, y, z]);
|
||||
}
|
||||
|
||||
Future zoomEnd() async {
|
||||
|
||||
@@ -38,15 +38,12 @@ class FilamentGestureDetector extends StatelessWidget {
|
||||
///
|
||||
final bool listenerEnabled;
|
||||
|
||||
final double zoomDelta;
|
||||
|
||||
const FilamentGestureDetector(
|
||||
{Key? key,
|
||||
required this.controller,
|
||||
this.child,
|
||||
this.showControlOverlay = false,
|
||||
this.listenerEnabled = true,
|
||||
this.zoomDelta = 1})
|
||||
this.listenerEnabled = true})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
||||
@@ -32,15 +32,12 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
|
||||
///
|
||||
final bool listenerEnabled;
|
||||
|
||||
final double zoomDelta;
|
||||
|
||||
const FilamentGestureDetectorDesktop(
|
||||
{Key? key,
|
||||
required this.controller,
|
||||
this.child,
|
||||
this.showControlOverlay = false,
|
||||
this.listenerEnabled = true,
|
||||
this.zoomDelta = 1})
|
||||
this.listenerEnabled = true})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@@ -71,15 +68,18 @@ class _FilamentGestureDetectorDesktopState
|
||||
///
|
||||
/// Scroll-wheel on desktop, interpreted as zoom
|
||||
///
|
||||
void _zoom(PointerScrollEvent pointerSignal) {
|
||||
void _zoom(PointerScrollEvent pointerSignal) async {
|
||||
_scrollTimer?.cancel();
|
||||
widget.controller.zoomBegin();
|
||||
widget.controller.zoomUpdate(pointerSignal.scrollDelta.dy > 0
|
||||
? widget.zoomDelta
|
||||
: -widget.zoomDelta);
|
||||
_scrollTimer = Timer(const Duration(milliseconds: 100), () {
|
||||
widget.controller.zoomEnd();
|
||||
_scrollTimer = null;
|
||||
await widget.controller.zoomBegin();
|
||||
await widget.controller.zoomUpdate(
|
||||
pointerSignal.localPosition.dx,
|
||||
pointerSignal.localPosition.dy,
|
||||
pointerSignal.scrollDelta.dy > 0 ? 1 : -1);
|
||||
|
||||
// we don't want to end the zoom in the same frame, because this will destroy the camera manipulator (and cancel the zoom update).
|
||||
// here, we just defer calling [zoomEnd] for 100ms to ensure the update is propagated through.
|
||||
_scrollTimer = Timer(Duration(milliseconds: 100), () async {
|
||||
await widget.controller.zoomEnd();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ class _FilamentGestureDetectorDesktopState
|
||||
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
|
||||
if (!_pointerMoving) {
|
||||
if (d.buttons == kTertiaryButton) {
|
||||
widget.controller.rotateStart(d.position.dx, d.position.dy);
|
||||
widget.controller
|
||||
.rotateStart(d.localPosition.dx, d.localPosition.dy);
|
||||
} else {
|
||||
widget.controller
|
||||
.panStart(d.localPosition.dx, d.localPosition.dy);
|
||||
@@ -117,7 +118,8 @@ class _FilamentGestureDetectorDesktopState
|
||||
// set the _pointerMoving flag so we don't call rotateStart/panStart on future move events
|
||||
_pointerMoving = true;
|
||||
if (d.buttons == kTertiaryButton) {
|
||||
widget.controller.rotateUpdate(d.position.dx, d.position.dy);
|
||||
widget.controller
|
||||
.rotateUpdate(d.localPosition.dx, d.localPosition.dy);
|
||||
} else {
|
||||
widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy);
|
||||
}
|
||||
|
||||
@@ -158,8 +158,8 @@ class _FilamentGestureDetectorMobileState
|
||||
},
|
||||
onScaleUpdate: (ScaleUpdateDetails d) async {
|
||||
if (d.pointerCount == 2) {
|
||||
widget.controller
|
||||
.zoomUpdate(d.horizontalScale > 1 ? 0.1 : -0.1);
|
||||
widget.controller.zoomUpdate(d.localFocalPoint.dx,
|
||||
d.localFocalPoint.dy, d.horizontalScale > 1 ? 0.1 : -0.1);
|
||||
} else if (!_scaling) {
|
||||
if (_rotateOnPointerMove) {
|
||||
widget.controller
|
||||
|
||||
@@ -64,7 +64,9 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
|
||||
|
||||
var markTextureFrameAvailable : @convention(c) (UnsafeMutableRawPointer?) -> () = { instancePtr in
|
||||
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(instancePtr!).takeUnretainedValue()
|
||||
instance.registry.textureFrameAvailable(instance.flutterTextureId!)
|
||||
if(instance.flutterTextureId != nil) {
|
||||
instance.registry.textureFrameAvailable(instance.flutterTextureId!)
|
||||
}
|
||||
}
|
||||
|
||||
public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
|
||||
@@ -142,6 +144,10 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
|
||||
Int(width), Int(height),
|
||||
0,
|
||||
&cvMetalTexture);
|
||||
if(cvret != 0) {
|
||||
result(FlutterError())
|
||||
return
|
||||
}
|
||||
metalTexture = CVMetalTextureGetTexture(cvMetalTexture!);
|
||||
let pixelBufferPtr = CVPixelBufferGetBaseAddress(pixelBuffer!);
|
||||
let pixelBufferAddress = Int(bitPattern:pixelBufferPtr);
|
||||
@@ -160,13 +166,7 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
|
||||
let args = call.arguments as! [Any]
|
||||
let width = UInt32(args[0] as! Int64)
|
||||
let height = UInt32(args[1] as! Int64)
|
||||
if(self.flutterTextureId != nil) {
|
||||
self.registry.unregisterTexture(self.flutterTextureId!)
|
||||
}
|
||||
createPixelBuffer(width: Int(width), height:Int(height))
|
||||
let metalTextureId = Int(bitPattern:Unmanaged.passUnretained(metalTexture!).toOpaque())
|
||||
print("Resized to \(args[0])x\(args[1])")
|
||||
result(self.flutterTextureId);
|
||||
result(FlutterMethodNotImplemented)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace polyvox {
|
||||
void loadPngTexture(string path, ResourceBuffer data);
|
||||
void loadTextureFromPath(string path);
|
||||
|
||||
|
||||
Manipulator<double>* _manipulator = nullptr;
|
||||
void _createManipulator();
|
||||
uint32_t _lastFrameTimeInNanos;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ A new Flutter plugin project.
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/*', 'src/*', 'include/filament/*', 'include/*', 'include/material/*.c'
|
||||
s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'include/filament/*', 'include/*', 'include/material/*.c'
|
||||
s.public_header_files = 'include/SwiftPolyvoxFilamentPlugin-Bridging-Header.h', 'include/PolyvoxFilamentApi.h', 'include/PolyvoxFilamentFFIApi.h', 'include/ResourceBuffer.hpp' #, 'include/filament/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
@@ -38,7 +38,7 @@ A new Flutter plugin project.
|
||||
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/include" "${PODS_TARGET_SRCROOT}/include/filament" "$(inherited)"',
|
||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lvkshaders -lbluegl -lbluevk -lbasis_transcoder -lmeshoptimizer',
|
||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lvkshaders -lbluegl -lbluevk -lbasis_transcoder -lmeshoptimizer',
|
||||
'LIBRARY_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/lib" "$(inherited)"',
|
||||
}
|
||||
s.swift_version = '5.0'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,32 +43,32 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->setBackgroundImage(path, fillHeight);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image_position(const void* const viewer, float x, float y, bool clamp) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image_position(const void* const viewer, float x, float y, bool clamp) {
|
||||
((FilamentViewer*)viewer)->setBackgroundImagePosition(x, y, clamp);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_tone_mapping(const void* const viewer, int toneMapping) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_tone_mapping(const void* const viewer, int toneMapping) {
|
||||
((FilamentViewer*)viewer)->setToneMapping((ToneMapping)toneMapping);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_bloom(const void* const viewer, float strength) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_bloom(const void* const viewer, float strength) {
|
||||
Log("Setting bloom to %f", strength);
|
||||
((FilamentViewer*)viewer)->setBloom(strength);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void load_skybox(const void* const viewer, const char* skyboxPath) {
|
||||
FLUTTER_PLUGIN_EXPORT void load_skybox(const void* const viewer, const char* skyboxPath) {
|
||||
((FilamentViewer*)viewer)->loadSkybox(skyboxPath);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void load_ibl(const void* const viewer, const char* iblPath, float intensity) {
|
||||
FLUTTER_PLUGIN_EXPORT void load_ibl(const void* const viewer, const char* iblPath, float intensity) {
|
||||
((FilamentViewer*)viewer)->loadIbl(iblPath, intensity);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->removeSkybox();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->removeIbl();
|
||||
}
|
||||
|
||||
@@ -76,11 +76,11 @@ extern "C" {
|
||||
return ((FilamentViewer*)viewer)->addLight((LightManager::Type)type, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_light(const void* const viewer, int32_t entityId) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_light(const void* const viewer, int32_t entityId) {
|
||||
((FilamentViewer*)viewer)->removeLight(entityId);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void clear_lights(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void clear_lights(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->clearLights();
|
||||
}
|
||||
|
||||
@@ -104,27 +104,27 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->moveCameraToAsset(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void* const viewer, float distance) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void* const viewer, float distance) {
|
||||
((FilamentViewer*)viewer)->setCameraFocusDistance(distance);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void* const viewer, float aperture, float shutterSpeed, float sensitivity) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void* const viewer, float aperture, float shutterSpeed, float sensitivity) {
|
||||
((FilamentViewer*)viewer)->setCameraExposure(aperture, shutterSpeed, sensitivity);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_position(const void* const viewer, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_position(const void* const viewer, float x, float y, float z) {
|
||||
((FilamentViewer*)viewer)->setCameraPosition(x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void* const viewer, float rads, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void* const viewer, float rads, float x, float y, float z) {
|
||||
((FilamentViewer*)viewer)->setCameraRotation(rads, x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void* const viewer, const float* const matrix) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void* const viewer, const float* const matrix) {
|
||||
((FilamentViewer*)viewer)->setCameraModelMatrix(matrix);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, float focalLength) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, float focalLength) {
|
||||
((FilamentViewer*)viewer)->setCameraFocalLength(focalLength);
|
||||
}
|
||||
|
||||
@@ -137,46 +137,46 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_frame_interval(
|
||||
FLUTTER_PLUGIN_EXPORT void set_frame_interval(
|
||||
const void* const viewer,
|
||||
float frameInterval
|
||||
) {
|
||||
((FilamentViewer*)viewer)->setFrameInterval(frameInterval);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->destroySwapChain();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const window, uint32_t width, uint32_t height) {
|
||||
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const window, uint32_t width, uint32_t height) {
|
||||
((FilamentViewer*)viewer)->createSwapChain(window, width, height);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(const void* const viewer, uint32_t width, uint32_t height, float scaleFactor) {
|
||||
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(const void* const viewer, uint32_t width, uint32_t height, float scaleFactor) {
|
||||
return ((FilamentViewer*)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_update(const void* const viewer, float x, float y, float delta) {
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_update(const void* const viewer, float x, float y, float delta) {
|
||||
((FilamentViewer*)viewer)->scrollUpdate(x, y, delta);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_begin(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_begin(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->scrollBegin();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_end(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void scroll_end(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->scrollEnd();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void grab_begin(const void* const viewer, float x, float y, bool pan) {
|
||||
FLUTTER_PLUGIN_EXPORT void grab_begin(const void* const viewer, float x, float y, bool pan) {
|
||||
((FilamentViewer*)viewer)->grabBegin(x, y, pan);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void grab_update(const void* const viewer, float x, float y) {
|
||||
FLUTTER_PLUGIN_EXPORT void grab_update(const void* const viewer, float x, float y) {
|
||||
((FilamentViewer*)viewer)->grabUpdate(x, y);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void grab_end(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void grab_end(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->grabEnd();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ extern "C" {
|
||||
return (void*)((FilamentViewer*)viewer)->getAssetManager();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void apply_weights(
|
||||
FLUTTER_PLUGIN_EXPORT void apply_weights(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
const char* const entityName,
|
||||
@@ -193,7 +193,7 @@ extern "C" {
|
||||
// ((AssetManager*)assetManager)->setMorphTargetWeights(asset, entityName, weights, count);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights(
|
||||
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
const char* const entityName,
|
||||
@@ -288,7 +288,7 @@ extern "C" {
|
||||
// }
|
||||
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void play_animation(
|
||||
FLUTTER_PLUGIN_EXPORT void play_animation(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
int index,
|
||||
@@ -299,7 +299,7 @@ extern "C" {
|
||||
((AssetManager*)assetManager)->playAnimation(asset, index, loop, reverse, replaceActive, crossfade);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_animation_frame(
|
||||
FLUTTER_PLUGIN_EXPORT void set_animation_frame(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
int animationIndex,
|
||||
@@ -319,7 +319,7 @@ extern "C" {
|
||||
return (int)names->size();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void get_animation_name(
|
||||
FLUTTER_PLUGIN_EXPORT void get_animation_name(
|
||||
void* assetManager,
|
||||
EntityId asset,
|
||||
char* const outPtr,
|
||||
@@ -335,17 +335,17 @@ extern "C" {
|
||||
return (int)names->size();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void* assetManager, EntityId asset, const char* meshName, char* const outPtr, int index ) {
|
||||
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void* assetManager, EntityId asset, const char* meshName, char* const outPtr, int index ) {
|
||||
unique_ptr<vector<string>> names = ((AssetManager*)assetManager)->getMorphTargetNames(asset, meshName);
|
||||
string name = names->at(index);
|
||||
strcpy(outPtr, name.c_str());
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void remove_asset(const void* const viewer, EntityId asset) {
|
||||
FLUTTER_PLUGIN_EXPORT void remove_asset(const void* const viewer, EntityId asset) {
|
||||
((FilamentViewer*)viewer)->removeAsset(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void clear_assets(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void clear_assets(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->clearAssets();
|
||||
}
|
||||
|
||||
@@ -353,23 +353,23 @@ extern "C" {
|
||||
return ((AssetManager*)assetManager)->setMaterialColor(asset, meshName, materialIndex, r, g, b, a);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void* assetManager, EntityId asset) {
|
||||
FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void* assetManager, EntityId asset) {
|
||||
((AssetManager*)assetManager)->transformToUnitCube(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_position(void* assetManager, EntityId asset, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_position(void* assetManager, EntityId asset, float x, float y, float z) {
|
||||
((AssetManager*)assetManager)->setPosition(asset, x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) {
|
||||
((AssetManager*)assetManager)->setRotation(asset, rads, x, y, z);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) {
|
||||
FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) {
|
||||
((AssetManager*)assetManager)->setScale(asset, scale);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void stop_animation(void* assetManager, EntityId asset, int index) {
|
||||
FLUTTER_PLUGIN_EXPORT void stop_animation(void* assetManager, EntityId asset, int index) {
|
||||
((AssetManager*)assetManager)->stopAnimation(asset, index);
|
||||
}
|
||||
|
||||
|
||||
98
macos/src/camutils/Bookmark.cpp
Normal file
98
macos/src/camutils/Bookmark.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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
|
||||
206
macos/src/camutils/FreeFlightManipulator.h
Normal file
206
macos/src/camutils/FreeFlightManipulator.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 */
|
||||
324
macos/src/camutils/Manipulator.cpp
Normal file
324
macos/src/camutils/Manipulator.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 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/Manipulator.h>
|
||||
|
||||
#include <math/scalar.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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
197
macos/src/camutils/MapManipulator.h
Normal file
197
macos/src/camutils/MapManipulator.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 <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 */
|
||||
201
macos/src/camutils/OrbitManipulator.h
Normal file
201
macos/src/camutils/OrbitManipulator.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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 */
|
||||
Reference in New Issue
Block a user