initial work to split into dart_filament and flutter_filament

This commit is contained in:
Nick Fisher
2024-04-30 12:07:26 +08:00
parent b81f34cd29
commit 8f9e309c34
1624 changed files with 165260 additions and 6619 deletions

View File

@@ -0,0 +1,638 @@
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
#include "filament/LightManager.h"
#include "Log.hpp"
#include "ThreadPool.hpp"
#include <thread>
#include <functional>
using namespace flutter_filament;
extern "C"
{
#include "DartFilamentApi.h"
FLUTTER_PLUGIN_EXPORT const void *create_filament_viewer(const void *context, const ResourceLoaderWrapper *const loader, void *const platform, const char *uberArchivePath)
{
return (const void *)new FilamentViewer(context, (const ResourceLoaderWrapperImpl *const)loader, platform, uberArchivePath);
}
FLUTTER_PLUGIN_EXPORT void create_render_target(const void *const viewer, intptr_t texture, uint32_t width, uint32_t height)
{
((FilamentViewer *)viewer)->createRenderTarget(texture, width, height);
}
FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer(const void *const viewer)
{
delete ((FilamentViewer *)viewer);
}
FLUTTER_PLUGIN_EXPORT void set_background_color(const void *const viewer, const float r, const float g, const float b, const float a)
{
((FilamentViewer *)viewer)->setBackgroundColor(r, g, b, a);
}
FLUTTER_PLUGIN_EXPORT void clear_background_image(const void *const viewer)
{
((FilamentViewer *)viewer)->clearBackgroundImage();
}
FLUTTER_PLUGIN_EXPORT void set_background_image(const void *const viewer, const char *path, bool fillHeight)
{
((FilamentViewer *)viewer)->setBackgroundImage(path, fillHeight);
}
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)
{
((FilamentViewer *)viewer)->setToneMapping((ToneMapping)toneMapping);
}
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)
{
((FilamentViewer *)viewer)->loadSkybox(skyboxPath);
}
FLUTTER_PLUGIN_EXPORT void load_ibl(const void *const viewer, const char *iblPath, float intensity)
{
((FilamentViewer *)viewer)->loadIbl(iblPath, intensity);
}
FLUTTER_PLUGIN_EXPORT void rotate_ibl(const void *const viewer, float *rotationMatrix)
{
math::mat3f matrix(rotationMatrix[0], rotationMatrix[1],
rotationMatrix[2],
rotationMatrix[3],
rotationMatrix[4],
rotationMatrix[5],
rotationMatrix[6],
rotationMatrix[7],
rotationMatrix[8]);
((FilamentViewer *)viewer)->rotateIbl(matrix);
}
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer)
{
((FilamentViewer *)viewer)->removeSkybox();
}
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void *const viewer)
{
((FilamentViewer *)viewer)->removeIbl();
}
EntityId add_light(const void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows)
{
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)
{
((FilamentViewer *)viewer)->removeLight(entityId);
}
FLUTTER_PLUGIN_EXPORT void clear_lights(const void *const viewer)
{
((FilamentViewer *)viewer)->clearLights();
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances)
{
return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances);
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer(void *sceneManager, const void *const data, size_t length)
{
return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t *)data, length);
}
FLUTTER_PLUGIN_EXPORT EntityId create_instance(void *sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->createInstance(entityId);
}
FLUTTER_PLUGIN_EXPORT int get_instance_count(void *sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->getInstanceCount(entityId);
}
FLUTTER_PLUGIN_EXPORT void get_instances(void *sceneManager, EntityId entityId, EntityId *out)
{
return ((SceneManager *)sceneManager)->getInstances(entityId, out);
}
FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath)
{
return ((SceneManager *)sceneManager)->loadGltf(assetPath, relativePath);
}
FLUTTER_PLUGIN_EXPORT void set_main_camera(const void *const viewer)
{
return ((FilamentViewer *)viewer)->setMainCamera();
}
FLUTTER_PLUGIN_EXPORT EntityId get_main_camera(const void *const viewer)
{
return ((FilamentViewer *)viewer)->getMainCamera();
}
FLUTTER_PLUGIN_EXPORT bool set_camera(const void *const viewer, EntityId asset, const char *nodeName)
{
return ((FilamentViewer *)viewer)->setCamera(asset, nodeName);
}
FLUTTER_PLUGIN_EXPORT void set_camera_fov(const void *const viewer, float fovInDegrees, float aspect)
{
return ((FilamentViewer *)viewer)->setCameraFov(double(fovInDegrees), double(aspect));
}
const double *const get_camera_model_matrix(const void *const viewer)
{
const auto &modelMatrix = ((FilamentViewer *)viewer)->getCameraModelMatrix();
double *array = (double *)calloc(16, sizeof(double));
memcpy(array, modelMatrix.asArray(), 16 * sizeof(double));
return array;
}
const double *const get_camera_view_matrix(const void *const viewer)
{
const auto &matrix = ((FilamentViewer *)viewer)->getCameraViewMatrix();
double *array = (double *)calloc(16, sizeof(double));
memcpy(array, matrix.asArray(), 16 * sizeof(double));
return array;
}
const double *const get_camera_projection_matrix(const void *const viewer)
{
const auto &matrix = ((FilamentViewer *)viewer)->getCameraProjectionMatrix();
double *array = (double *)calloc(16, sizeof(double));
memcpy(array, matrix.asArray(), 16 * sizeof(double));
return array;
}
const double *const get_camera_culling_projection_matrix(const void *const viewer)
{
const auto &matrix = ((FilamentViewer *)viewer)->getCameraCullingProjectionMatrix();
double *array = (double *)calloc(16, sizeof(double));
memcpy(array, matrix.asArray(), 16 * sizeof(double));
return array;
}
void set_camera_projection_matrix(const void *const viewer, const double *const matrix, double near, double far)
{
((FilamentViewer *)viewer)->setCameraProjectionMatrix(matrix, near, far);
}
void set_camera_culling(const void *const viewer, double near, double far)
{
((FilamentViewer *)viewer)->setCameraCulling(near, far);
}
double get_camera_culling_near(const void *const viewer)
{
return ((FilamentViewer *)viewer)->getCameraCullingNear();
}
double get_camera_culling_far(const void *const viewer)
{
return ((FilamentViewer *)viewer)->getCameraCullingFar();
}
const double *const get_camera_frustum(const void *const viewer)
{
const auto frustum = ((FilamentViewer *)viewer)->getCameraFrustum();
const math::float4 *planes = frustum.getNormalizedPlanes();
double *array = (double *)calloc(24, sizeof(double));
for (int i = 0; i < 6; i++)
{
auto plane = planes[i];
array[i * 4] = double(plane.x);
array[i * 4 + 1] = double(plane.y);
array[i * 4 + 2] = double(plane.z);
array[i * 4 + 3] = double(plane.w);
}
return array;
}
FLUTTER_PLUGIN_EXPORT void set_camera_manipulator_options(const void *const viewer, _ManipulatorMode mode, double orbitSpeedX, double orbitSpeedY, double zoomSpeed)
{
((FilamentViewer *)viewer)->setCameraManipulatorOptions((filament::camutils::Mode)mode, orbitSpeedX, orbitSpeedY, zoomSpeed);
}
FLUTTER_PLUGIN_EXPORT void set_view_frustum_culling(const void *const viewer, bool enabled)
{
((FilamentViewer *)viewer)->setViewFrustumCulling(enabled);
}
FLUTTER_PLUGIN_EXPORT void move_camera_to_asset(const void *const viewer, EntityId asset)
{
((FilamentViewer *)viewer)->moveCameraToAsset(asset);
}
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)
{
((FilamentViewer *)viewer)->setCameraExposure(aperture, shutterSpeed, sensitivity);
}
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 w, float x, float y, float z)
{
((FilamentViewer *)viewer)->setCameraRotation(w, x, y, z);
}
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)
{
((FilamentViewer *)viewer)->setCameraFocalLength(focalLength);
}
FLUTTER_PLUGIN_EXPORT void render(
const void *const viewer,
uint64_t frameTimeInNanos,
void *pixelBuffer,
void (*callback)(void *buf, size_t size, void *data),
void *data)
{
((FilamentViewer *)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data);
}
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)
{
((FilamentViewer *)viewer)->destroySwapChain();
}
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)
{
return ((FilamentViewer *)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor);
}
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)
{
((FilamentViewer *)viewer)->scrollBegin();
}
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)
{
((FilamentViewer *)viewer)->grabBegin(x, y, pan);
}
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)
{
((FilamentViewer *)viewer)->grabEnd();
}
FLUTTER_PLUGIN_EXPORT void *get_scene_manager(const void *const viewer)
{
return (void *)((FilamentViewer *)viewer)->getSceneManager();
}
FLUTTER_PLUGIN_EXPORT void apply_weights(
void *sceneManager,
EntityId asset,
const char *const entityName,
float *const weights,
int count)
{
// ((SceneManager*)sceneManager)->setMorphTargetWeights(asset, entityName, weights, count);
}
FLUTTER_PLUGIN_EXPORT bool set_morph_target_weights(
void *sceneManager,
EntityId asset,
const float *const weights,
const int numWeights)
{
return ((SceneManager *)sceneManager)->setMorphTargetWeights(asset, weights, numWeights);
}
bool set_morph_animation(
void *sceneManager,
EntityId asset,
const float *const morphData,
const int *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs)
{
return ((SceneManager *)sceneManager)->setMorphAnimationBuffer(asset, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
}
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose(void *sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->resetBones(entityId);
}
FLUTTER_PLUGIN_EXPORT void add_bone_animation(
void *sceneManager,
EntityId asset,
const float *const frameData,
int numFrames,
const char *const boneName,
const char **const meshNames,
int numMeshTargets,
float frameLengthInMs,
bool isModelSpace)
{
((SceneManager *)sceneManager)->addBoneAnimation(asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace);
}
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled)
{
((FilamentViewer *)viewer)->setPostProcessing(enabled);
}
FLUTTER_PLUGIN_EXPORT void set_antialiasing(void *const viewer, bool msaa, bool fxaa, bool taa)
{
((FilamentViewer *)viewer)->setAntiAliasing(msaa, fxaa, taa);
}
FLUTTER_PLUGIN_EXPORT bool set_bone_transform(
void *sceneManager,
EntityId entityId,
const char *entityName,
const float *const transform,
const char *boneName)
{
auto matrix = math::mat4f(
transform[0], transform[1], transform[2],
transform[3],
transform[4],
transform[5],
transform[6],
transform[7],
transform[8],
transform[9],
transform[10],
transform[11],
transform[12],
transform[13],
transform[14],
transform[15]);
return ((SceneManager *)sceneManager)->setBoneTransform(entityId, entityName, 0, boneName, matrix);
}
FLUTTER_PLUGIN_EXPORT void play_animation(
void *sceneManager,
EntityId asset,
int index,
bool loop,
bool reverse,
bool replaceActive,
float crossfade)
{
((SceneManager *)sceneManager)->playAnimation(asset, index, loop, reverse, replaceActive, crossfade);
}
FLUTTER_PLUGIN_EXPORT void set_animation_frame(
void *sceneManager,
EntityId asset,
int animationIndex,
int animationFrame)
{
// ((SceneManager*)sceneManager)->setAnimationFrame(asset, animationIndex, animationFrame);
}
float get_animation_duration(void *sceneManager, EntityId asset, int animationIndex)
{
return ((SceneManager *)sceneManager)->getAnimationDuration(asset, animationIndex);
}
int get_animation_count(
void *sceneManager,
EntityId asset)
{
auto names = ((SceneManager *)sceneManager)->getAnimationNames(asset);
return (int)names->size();
}
FLUTTER_PLUGIN_EXPORT void get_animation_name(
void *sceneManager,
EntityId asset,
char *const outPtr,
int index)
{
auto names = ((SceneManager *)sceneManager)->getAnimationNames(asset);
std::string name = names->at(index);
strcpy(outPtr, name.c_str());
}
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count(void *sceneManager, EntityId asset, const char *meshName)
{
auto names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
return (int)names->size();
}
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void *sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index)
{
auto names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
std::string name = names->at(index);
strcpy(outPtr, name.c_str());
}
FLUTTER_PLUGIN_EXPORT void remove_entity(const void *const viewer, EntityId asset)
{
((FilamentViewer *)viewer)->removeEntity(asset);
}
FLUTTER_PLUGIN_EXPORT void clear_entities(const void *const viewer)
{
((FilamentViewer *)viewer)->clearEntities();
}
bool set_material_color(void *sceneManager, EntityId asset, const char *meshName, int materialIndex, const float r, const float g, const float b, const float a)
{
return ((SceneManager *)sceneManager)->setMaterialColor(asset, meshName, materialIndex, r, g, b, a);
}
FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void *sceneManager, EntityId asset)
{
((SceneManager *)sceneManager)->transformToUnitCube(asset);
}
FLUTTER_PLUGIN_EXPORT void set_position(void *sceneManager, EntityId asset, float x, float y, float z)
{
((SceneManager *)sceneManager)->setPosition(asset, x, y, z);
}
FLUTTER_PLUGIN_EXPORT void set_rotation(void *sceneManager, EntityId asset, float rads, float x, float y, float z, float w)
{
((SceneManager *)sceneManager)->setRotation(asset, rads, x, y, z, w);
}
FLUTTER_PLUGIN_EXPORT void set_scale(void *sceneManager, EntityId asset, float scale)
{
((SceneManager *)sceneManager)->setScale(asset, scale);
}
FLUTTER_PLUGIN_EXPORT void queue_position_update(void *sceneManager, EntityId asset, float x, float y, float z, bool relative)
{
((SceneManager *)sceneManager)->queuePositionUpdate(asset, x, y, z, relative);
}
FLUTTER_PLUGIN_EXPORT void queue_rotation_update(void *sceneManager, EntityId asset, float rads, float x, float y, float z, float w, bool relative)
{
((SceneManager *)sceneManager)->queueRotationUpdate(asset, rads, x, y, z, w, relative);
}
FLUTTER_PLUGIN_EXPORT void stop_animation(void *sceneManager, EntityId asset, int index)
{
((SceneManager *)sceneManager)->stopAnimation(asset, index);
}
FLUTTER_PLUGIN_EXPORT int hide_mesh(void *sceneManager, EntityId asset, const char *meshName)
{
return ((SceneManager *)sceneManager)->hide(asset, meshName);
}
FLUTTER_PLUGIN_EXPORT int reveal_mesh(void *sceneManager, EntityId asset, const char *meshName)
{
return ((SceneManager *)sceneManager)->reveal(asset, meshName);
}
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, void (*callback)(EntityId entityId, int x, int y))
{
((FilamentViewer *)viewer)->pick(static_cast<uint32_t>(x), static_cast<uint32_t>(y), callback);
}
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const sceneManager, const EntityId entityId)
{
return ((SceneManager *)sceneManager)->getNameForEntity(entityId);
}
FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const sceneManager, const EntityId target, bool renderableOnly)
{
return ((SceneManager *)sceneManager)->getEntityCount(target, renderableOnly);
}
FLUTTER_PLUGIN_EXPORT void get_entities(void *const sceneManager, const EntityId target, bool renderableOnly, EntityId *out)
{
((SceneManager *)sceneManager)->getEntities(target, renderableOnly, out);
}
FLUTTER_PLUGIN_EXPORT const char *get_entity_name_at(void *const sceneManager, const EntityId target, int index, bool renderableOnly)
{
return ((SceneManager *)sceneManager)->getEntityNameAt(target, index, renderableOnly);
}
FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording)
{
((FilamentViewer *)viewer)->setRecording(recording);
}
FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char *outputDirectory)
{
((FilamentViewer *)viewer)->setRecordingOutputDirectory(outputDirectory);
}
FLUTTER_PLUGIN_EXPORT void ios_dummy()
{
Log("Dummy called");
}
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr)
{
free(ptr);
}
FLUTTER_PLUGIN_EXPORT void add_collision_component(void *const sceneManager, EntityId entityId, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform)
{
((SceneManager *)sceneManager)->addCollisionComponent(entityId, onCollisionCallback, affectsCollidingTransform);
}
FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->removeCollisionComponent(entityId);
}
FLUTTER_PLUGIN_EXPORT bool add_animation_component(void *const sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->addAnimationComponent(entityId);
}
FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath)
{
return ((FilamentViewer *)viewer)->createGeometry(vertices, (uint32_t)numVertices, indices, numIndices, (filament::RenderableManager::PrimitiveType)primitiveType, materialPath);
}
FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char *name)
{
auto entity = ((SceneManager *)sceneManager)->findChildEntityByName(parent, name);
return utils::Entity::smuggle(entity);
}
FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent)
{
((SceneManager *)sceneManager)->setParent(child, parent);
}
FLUTTER_PLUGIN_EXPORT void test_collisions(void *const sceneManager, EntityId entity)
{
((SceneManager *)sceneManager)->testCollisions(entity);
}
FLUTTER_PLUGIN_EXPORT void set_priority(void *const sceneManager, EntityId entity, int priority)
{
((SceneManager *)sceneManager)->setPriority(entity, priority);
}
FLUTTER_PLUGIN_EXPORT void get_gizmo(void *const sceneManager, EntityId *out)
{
return ((SceneManager *)sceneManager)->getGizmo(out);
}
}

View File

@@ -0,0 +1,655 @@
#include "DartFilamentFFIApi.h"
#include "FilamentViewer.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "filament/LightManager.h"
#include <functional>
#include <mutex>
#include <thread>
#include <stdlib.h>
#ifdef __EMSCRIPTEN__
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
extern "C"
{
extern FLUTTER_PLUGIN_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE flutter_filament_web_create_gl_context();
}
#include <pthread.h>
#endif
using namespace flutter_filament;
using namespace std::chrono_literals;
class RenderLoop
{
public:
explicit RenderLoop()
{
_t = new std::thread([this]()
{
auto last = std::chrono::high_resolution_clock::now();
while (!_stop) {
if (_rendering) {
// auto frameStart = std::chrono::high_resolution_clock::now();
doRender();
// auto frameEnd = std::chrono::high_resolution_clock::now();
}
last = std::chrono::high_resolution_clock::now();
auto now = std::chrono::high_resolution_clock::now();
float elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
std::function<void()> task;
std::unique_lock<std::mutex> lock(_access);
if(_tasks.empty()) {
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
}
while(!_tasks.empty()) {
task = std::move(_tasks.front());
_tasks.pop_front();
task();
}
now = std::chrono::high_resolution_clock::now();
elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
if(elapsed < _frameIntervalInMilliseconds) {
auto sleepFor = std::chrono::microseconds(int(_frameIntervalInMilliseconds - elapsed) * 1000);
std::this_thread::sleep_for(sleepFor);
}
} });
}
~RenderLoop()
{
_stop = true;
_t->join();
}
void createViewer(void *const context, void *const platform,
const char *uberArchivePath,
const ResourceLoaderWrapperImpl *const loader,
void (*renderCallback)(void *),
void *const owner,
void (*callback)(void *const))
{
_renderCallback = renderCallback;
_renderCallbackOwner = owner;
std::packaged_task<FilamentViewer *()> lambda([=]() mutable
{
#ifdef __EMSCRIPTEN__
auto emContext = flutter_filament_web_create_gl_context();
auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)emContext);
if(success != EMSCRIPTEN_RESULT_SUCCESS) {
std::cout << "Failed to make context current." << std::endl;
return (FilamentViewer*)nullptr;
}
_viewer = new FilamentViewer((void* const) emContext, loader, platform, uberArchivePath);
#else
_viewer = new FilamentViewer(context, loader, platform, uberArchivePath);
#endif
callback(_viewer);
return _viewer; });
auto fut = add_task(lambda);
}
void destroyViewer()
{
std::packaged_task<void()> lambda([=]() mutable
{
_rendering = false;
destroy_filament_viewer(_viewer);
_viewer = nullptr; });
auto fut = add_task(lambda);
}
void setRendering(bool rendering)
{
std::packaged_task<void()> lambda(
[=]() mutable
{ this->_rendering = rendering; });
auto fut = add_task(lambda);
}
void doRender()
{
// auto now = std::chrono::high_resolution_clock::now();
// auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
render(_viewer, 0, nullptr, nullptr, nullptr);
_lastRenderTime = std::chrono::high_resolution_clock::now();
if (_renderCallback)
{
_renderCallback(_renderCallbackOwner);
}
#ifdef __EMSCRIPTEN__
emscripten_webgl_commit_frame();
#endif
}
void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds)
{
_frameIntervalInMilliseconds = frameIntervalInMilliseconds;
Log("Set _frameIntervalInMilliseconds to %f", _frameIntervalInMilliseconds);
}
template <class Rt>
auto add_task(std::packaged_task<Rt()> &pt) -> std::future<Rt>
{
std::unique_lock<std::mutex> lock(_access);
auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(
std::move(pt))]
{ (*pt)(); });
_cond.notify_one();
return ret;
}
private:
bool _stop = false;
bool _rendering = false;
float _frameIntervalInMilliseconds = 1000.0 / 60.0;
std::mutex _access;
FilamentViewer *_viewer = nullptr;
void (*_renderCallback)(void *const) = nullptr;
void *_renderCallbackOwner = nullptr;
std::thread *_t = nullptr;
std::condition_variable _cond;
std::deque<std::function<void()>> _tasks;
std::chrono::steady_clock::time_point _lastRenderTime = std::chrono::high_resolution_clock::now();
};
extern "C"
{
static RenderLoop *_rl;
FLUTTER_PLUGIN_EXPORT void create_filament_viewer_ffi(
void *const context, void *const platform, const char *uberArchivePath,
const ResourceLoaderWrapper *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(void *const))
{
if (!_rl)
{
_rl = new RenderLoop();
}
_rl->createViewer(context, platform, uberArchivePath, (const ResourceLoaderWrapperImpl *const)loader,
renderCallback, renderCallbackOwner, callback);
}
FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer)
{
_rl->destroyViewer();
}
FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void *const viewer,
void *const surface,
uint32_t width,
uint32_t height,
void (*onComplete)())
{
Log("Creating swapchain %dx%d with viewer %d", width, height, viewer);
std::packaged_task<void()> lambda(
[=]() mutable
{
create_swap_chain(viewer, surface, width, height);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)())
{
Log("Destroying swapchain");
std::packaged_task<void()> lambda(
[=]() mutable
{
destroy_swap_chain(viewer);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void *const viewer,
intptr_t nativeTextureId,
uint32_t width,
uint32_t height,
void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{
create_render_target(viewer, nativeTextureId, width, height);
onComplete(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi(
void *const viewer, const uint32_t width, const uint32_t height,
const float scaleFactor,
void (*onComplete)())
{
Log("Update viewport %dx%d", width, height);
std::packaged_task<void()> lambda([=]() mutable
{
update_viewport_and_camera_projection(viewer, width, height, scaleFactor);
onComplete(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void *const viewer,
bool rendering)
{
if (!_rl)
{
Log("No render loop!"); // PANIC?
}
else
{
if (rendering)
{
Log("Set rendering to true");
}
else
{
Log("Set rendering to false");
}
_rl->setRendering(rendering);
}
}
FLUTTER_PLUGIN_EXPORT void
set_frame_interval_ffi(float frameIntervalInMilliseconds)
{
_rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds);
}
FLUTTER_PLUGIN_EXPORT void render_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]() mutable
{ _rl->doRender(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void
set_background_color_ffi(void *const viewer, const float r, const float g,
const float b, const float a)
{
std::packaged_task<void()> lambda(
[=]() mutable
{ set_background_color(viewer, r, g, b, a); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void load_gltf_ffi(void *const sceneManager,
const char *path,
const char *relativeResourcePath,
void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda([=]() mutable
{
auto entity = load_gltf(sceneManager, path, relativeResourcePath);
callback(entity);
return entity; });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void load_glb_ffi(void *const sceneManager,
const char *path, int numInstances, void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda(
[=]() mutable
{
auto entity = load_glb(sceneManager, path, numInstances);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void load_glb_from_buffer_ffi(void *const sceneManager,
const void *const data, size_t length, int numInstances, void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda(
[=]() mutable
{
auto entity = load_glb_from_buffer(sceneManager, data, length);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ clear_background_image(viewer); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void *const viewer,
const char *path,
bool fillHeight, void (*callback)())
{
std::packaged_task<void()> lambda(
[=]
{
set_background_image(viewer, path, fillHeight);
callback();
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void *const viewer,
float x, float y,
bool clamp)
{
std::packaged_task<void()> lambda(
[=]
{ set_background_image_position(viewer, x, y, clamp); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void *const viewer,
int toneMapping)
{
std::packaged_task<void()> lambda(
[=]
{ set_tone_mapping(viewer, toneMapping); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void *const viewer, float strength)
{
std::packaged_task<void()> lambda([=]
{ set_bloom(viewer, strength); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void *const viewer,
const char *skyboxPath,
void (*onComplete)())
{
std::packaged_task<void()> lambda([=]
{
load_skybox(viewer, skyboxPath);
onComplete(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void *const viewer, const char *iblPath,
float intensity)
{
std::packaged_task<void()> lambda(
[=]
{ load_ibl(viewer, iblPath, intensity); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ remove_skybox(viewer); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ remove_ibl(viewer); });
auto fut = _rl->add_task(lambda);
}
void add_light_ffi(void *const viewer, uint8_t type, float colour,
float intensity, float posX, float posY, float posZ,
float dirX, float dirY, float dirZ, bool shadows, void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda([=]
{
auto entity = add_light(viewer, type, colour, intensity, posX, posY, posZ, dirX,
dirY, dirZ, shadows);
callback(entity);
return entity; });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void *const viewer,
EntityId entityId)
{
std::packaged_task<void()> lambda([=]
{ remove_light(viewer, entityId); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ clear_lights(viewer); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void *const viewer,
EntityId asset, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
remove_entity(viewer, asset);
callback(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
clear_entities(viewer);
callback(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_camera_ffi(void *const viewer, EntityId asset,
const char *nodeName, void (*callback)(bool))
{
std::packaged_task<bool()> lambda(
[=]
{
auto success = set_camera(viewer, asset, nodeName);
callback(success);
return success;
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void
get_morph_target_name_ffi(void *sceneManager, EntityId asset,
const char *meshName, char *const outPtr, int index, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
get_morph_target_name(sceneManager, asset, meshName, outPtr, index);
callback(); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void
get_morph_target_name_count_ffi(void *sceneManager, EntityId asset,
const char *meshName, void (*callback)(int))
{
std::packaged_task<int()> lambda([=]
{
auto count = get_morph_target_name_count(sceneManager, asset, meshName);
callback(count);
return count; });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager,
EntityId asset, int index,
bool loop, bool reverse,
bool replaceActive,
float crossfade)
{
std::packaged_task<void()> lambda([=]
{ play_animation(sceneManager, asset, index, loop, reverse, replaceActive,
crossfade); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const sceneManager,
EntityId asset,
int animationIndex,
int animationFrame)
{
std::packaged_task<void()> lambda([=]
{ set_animation_frame(sceneManager, asset, animationIndex, animationFrame); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const sceneManager,
EntityId asset, int index)
{
std::packaged_task<void()> lambda(
[=]
{ stop_animation(sceneManager, asset, index); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void get_animation_count_ffi(void *const sceneManager,
EntityId asset,
void (*callback)(int))
{
std::packaged_task<int()> lambda(
[=]
{
auto count = get_animation_count(sceneManager, asset);
callback(count);
return count;
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const sceneManager,
EntityId asset,
char *const outPtr,
int index,
void (*callback)())
{
std::packaged_task<void()> lambda(
[=]
{
get_animation_name(sceneManager, asset, outPtr, index);
callback();
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer,
bool enabled)
{
std::packaged_task<void()> lambda(
[=]
{ set_post_processing(viewer, enabled); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void
get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId, void (*callback)(const char *))
{
std::packaged_task<const char *()> lambda(
[=]
{
auto name = get_name_for_entity(sceneManager, entityId);
callback(name);
return name;
});
auto fut = _rl->add_task(lambda);
}
void set_morph_target_weights_ffi(void *const sceneManager,
EntityId asset,
const float *const morphData,
int numWeights,
void (*callback)(bool))
{
std::packaged_task<void()> lambda(
[=]
{
auto result = set_morph_target_weights(sceneManager, asset, morphData, numWeights);
callback(result);
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_bone_transform_ffi(
void *sceneManager,
EntityId asset,
const char *entityName,
const float *const transform,
const char *boneName,
void (*callback)(bool))
{
std::packaged_task<bool()> lambda(
[=]
{
auto success = set_bone_transform(sceneManager, asset, entityName, transform, boneName);
callback(success);
return success;
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId)
{
std::packaged_task<void()> lambda(
[=]
{ return reset_to_rest_pose(sceneManager, entityId); });
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *sceneManager,
EntityId asset,
const float *const frameData,
int numFrames,
const char *const boneName,
const char **const meshNames,
int numMeshTargets,
float frameLengthInMs,
bool isModelSpace)
{
std::packaged_task<void()> lambda(
[=]
{
add_bone_animation(sceneManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace);
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath, void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda(
[=]
{
auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, primitiveType, materialPath);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
#include <streambuf>
#include <functional>
#include <cassert>
#include <cstring>
namespace flutter_filament {
class StreamBufferAdapter : public std::streambuf
{
public:
StreamBufferAdapter(const char *begin, const char *end);
~StreamBufferAdapter() {
}
std::streamsize size();
private:
int_type uflow() override;
int_type underflow() override;
int_type pbackfail(int_type ch) override;
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) override;
std::streampos seekpos(std::streampos sp, std::ios_base::openmode which) override;
std::streamsize showmanyc() override;
};
StreamBufferAdapter::StreamBufferAdapter(const char *begin, const char *end)
{
setg((char*)begin, (char*)begin, (char*)end);
}
std::streamsize StreamBufferAdapter::size() {
return egptr() - eback();
}
std::streambuf::int_type StreamBufferAdapter::underflow()
{
if (gptr() == egptr()) {
return traits_type::eof();
}
return *(gptr());
}
std::streambuf::int_type StreamBufferAdapter::uflow()
{
if (gptr() == egptr()) {
return traits_type::eof();
}
gbump(1);
return *(gptr());
}
std::streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch)
{
if (gptr() == eback() || (ch != traits_type::eof() && ch != gptr()[-1]))
return traits_type::eof();
gbump(-ch);
return *(gptr());
}
std::streamsize StreamBufferAdapter::showmanyc()
{
return egptr() - gptr();
}
std::streampos StreamBufferAdapter::seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in) {
if(way == std::ios_base::beg) {
setg(eback(), eback()+off, egptr());
} else if(way == std::ios_base::cur) {
gbump((int)off);
} else {
setg(eback(), egptr()-off, egptr());
}
return gptr() - eback();
}
std::streampos StreamBufferAdapter::seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in) {
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
}
}

View File

@@ -0,0 +1,30 @@
#include "TimeIt.hpp"
#if __cplusplus <= 199711L && !_WIN32
void Timer::reset()
{
clock_gettime(CLOCK_REALTIME, &beg_);
}
double Timer::elapsed()
{
clock_gettime(CLOCK_REALTIME, &end_);
return end_.tv_sec - beg_.tv_sec +
(end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
}
#else
void Timer::reset()
{
beg_ = clock_::now();
}
double Timer::elapsed()
{
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count();
}
#endif

View 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

View 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 */

View File

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

View File

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

View File

@@ -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 */