renaming to Thermion

This commit is contained in:
Nick Fisher
2024-06-15 15:05:34 +08:00
parent 1a5f573bc0
commit fe62a70e29
719 changed files with 7291 additions and 3946 deletions

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 thermion_flutter {
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,816 @@
#ifdef _WIN32
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "opengl32.lib")
#endif
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
#include "filament/LightManager.h"
#include "Log.hpp"
#include "ThreadPool.hpp"
#include <thread>
#include <functional>
using namespace thermion_flutter;
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
extern "C"
{
#include "ThermionDartApi.h"
EMSCRIPTEN_KEEPALIVE const void *create_filament_viewer(const void *context, const void *const loader, void *const platform, const char *uberArchivePath)
{
auto viewer = (const void *)new FilamentViewer(context, (const ResourceLoaderWrapperImpl *const)loader, platform, uberArchivePath);
return viewer;
}
EMSCRIPTEN_KEEPALIVE void create_render_target(const void *const viewer, intptr_t texture, uint32_t width, uint32_t height)
{
((FilamentViewer *)viewer)->createRenderTarget(texture, width, height);
}
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer(const void *const viewer)
{
delete ((FilamentViewer *)viewer);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void clear_background_image(const void *const viewer)
{
((FilamentViewer *)viewer)->clearBackgroundImage();
}
EMSCRIPTEN_KEEPALIVE void set_background_image(const void *const viewer, const char *path, bool fillHeight)
{
((FilamentViewer *)viewer)->setBackgroundImage(path, fillHeight);
}
EMSCRIPTEN_KEEPALIVE void set_background_image_position(const void *const viewer, float x, float y, bool clamp)
{
((FilamentViewer *)viewer)->setBackgroundImagePosition(x, y, clamp);
}
EMSCRIPTEN_KEEPALIVE void set_tone_mapping(const void *const viewer, int toneMapping)
{
((FilamentViewer *)viewer)->setToneMapping((ToneMapping)toneMapping);
}
EMSCRIPTEN_KEEPALIVE void set_bloom(const void *const viewer, float strength)
{
Log("Setting bloom to %f", strength);
((FilamentViewer *)viewer)->setBloom(strength);
}
EMSCRIPTEN_KEEPALIVE void load_skybox(const void *const viewer, const char *skyboxPath)
{
((FilamentViewer *)viewer)->loadSkybox(skyboxPath);
}
EMSCRIPTEN_KEEPALIVE void load_ibl(const void *const viewer, const char *iblPath, float intensity)
{
((FilamentViewer *)viewer)->loadIbl(iblPath, intensity);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void remove_skybox(const void *const viewer)
{
((FilamentViewer *)viewer)->removeSkybox();
}
EMSCRIPTEN_KEEPALIVE 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,
float falloffRadius,
float spotLightConeInner,
float spotLightConeOuter,
float sunAngularRadius,
float sunHaloSize,
float sunHaloFallof,
bool shadows)
{
return ((FilamentViewer *)viewer)->addLight(
(LightManager::Type)type,
colour,
intensity,
posX,
posY,
posZ,
dirX,
dirY,
dirZ,
falloffRadius,
spotLightConeInner,
spotLightConeOuter,
sunAngularRadius,
sunHaloSize,
sunHaloFallof,
shadows);
}
EMSCRIPTEN_KEEPALIVE void remove_light(const void *const viewer, int32_t entityId)
{
((FilamentViewer *)viewer)->removeLight(entityId);
}
EMSCRIPTEN_KEEPALIVE void clear_lights(const void *const viewer)
{
((FilamentViewer *)viewer)->clearLights();
}
EMSCRIPTEN_KEEPALIVE EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances)
{
return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances);
}
EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(void *sceneManager, const void *const data, size_t length)
{
return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t *)data, length);
}
EMSCRIPTEN_KEEPALIVE EntityId create_instance(void *sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->createInstance(entityId);
}
EMSCRIPTEN_KEEPALIVE int get_instance_count(void *sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->getInstanceCount(entityId);
}
EMSCRIPTEN_KEEPALIVE void get_instances(void *sceneManager, EntityId entityId, EntityId *out)
{
return ((SceneManager *)sceneManager)->getInstances(entityId, out);
}
EMSCRIPTEN_KEEPALIVE EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath)
{
return ((SceneManager *)sceneManager)->loadGltf(assetPath, relativePath);
}
EMSCRIPTEN_KEEPALIVE void set_main_camera(const void *const viewer)
{
return ((FilamentViewer *)viewer)->setMainCamera();
}
EMSCRIPTEN_KEEPALIVE EntityId get_main_camera(const void *const viewer)
{
return ((FilamentViewer *)viewer)->getMainCamera();
}
EMSCRIPTEN_KEEPALIVE bool set_camera(const void *const viewer, EntityId asset, const char *nodeName)
{
return ((FilamentViewer *)viewer)->setCamera(asset, nodeName);
}
EMSCRIPTEN_KEEPALIVE 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;
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void set_view_frustum_culling(const void *const viewer, bool enabled)
{
((FilamentViewer *)viewer)->setViewFrustumCulling(enabled);
}
EMSCRIPTEN_KEEPALIVE void move_camera_to_asset(const void *const viewer, EntityId asset)
{
((FilamentViewer *)viewer)->moveCameraToAsset(asset);
}
EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(const void *const viewer, float distance)
{
((FilamentViewer *)viewer)->setCameraFocusDistance(distance);
}
EMSCRIPTEN_KEEPALIVE void set_camera_exposure(const void *const viewer, float aperture, float shutterSpeed, float sensitivity)
{
((FilamentViewer *)viewer)->setCameraExposure(aperture, shutterSpeed, sensitivity);
}
EMSCRIPTEN_KEEPALIVE void set_camera_position(const void *const viewer, float x, float y, float z)
{
((FilamentViewer *)viewer)->setCameraPosition(x, y, z);
}
EMSCRIPTEN_KEEPALIVE void set_camera_rotation(const void *const viewer, float w, float x, float y, float z)
{
((FilamentViewer *)viewer)->setCameraRotation(w, x, y, z);
}
EMSCRIPTEN_KEEPALIVE void set_camera_model_matrix(const void *const viewer, const float *const matrix)
{
((FilamentViewer *)viewer)->setCameraModelMatrix(matrix);
}
EMSCRIPTEN_KEEPALIVE void set_camera_focal_length(const void *const viewer, float focalLength)
{
((FilamentViewer *)viewer)->setCameraFocalLength(focalLength);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void set_frame_interval(
const void *const viewer,
float frameInterval)
{
((FilamentViewer *)viewer)->setFrameInterval(frameInterval);
}
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain(const void *const viewer)
{
((FilamentViewer *)viewer)->destroySwapChain();
}
EMSCRIPTEN_KEEPALIVE void create_swap_chain(const void *const viewer, const void *const window, uint32_t width, uint32_t height)
{
((FilamentViewer *)viewer)->createSwapChain(window, width, height);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void scroll_update(const void *const viewer, float x, float y, float delta)
{
((FilamentViewer *)viewer)->scrollUpdate(x, y, delta);
}
EMSCRIPTEN_KEEPALIVE void scroll_begin(const void *const viewer)
{
((FilamentViewer *)viewer)->scrollBegin();
}
EMSCRIPTEN_KEEPALIVE void scroll_end(const void *const viewer)
{
((FilamentViewer *)viewer)->scrollEnd();
}
EMSCRIPTEN_KEEPALIVE void grab_begin(const void *const viewer, float x, float y, bool pan)
{
((FilamentViewer *)viewer)->grabBegin(x, y, pan);
}
EMSCRIPTEN_KEEPALIVE void grab_update(const void *const viewer, float x, float y)
{
((FilamentViewer *)viewer)->grabUpdate(x, y);
}
EMSCRIPTEN_KEEPALIVE void grab_end(const void *const viewer)
{
((FilamentViewer *)viewer)->grabEnd();
}
EMSCRIPTEN_KEEPALIVE void *get_scene_manager(const void *const viewer)
{
return (void *)((FilamentViewer *)viewer)->getSceneManager();
}
EMSCRIPTEN_KEEPALIVE void apply_weights(
void *sceneManager,
EntityId asset,
const char *const entityName,
float *const weights,
int count)
{
// ((SceneManager*)sceneManager)->setMorphTargetWeights(asset, entityName, weights, count);
}
EMSCRIPTEN_KEEPALIVE 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)
{
auto result = ((SceneManager *)sceneManager)->setMorphAnimationBuffer(asset, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
return result;
}
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose(void *sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->resetBones(entityId);
}
EMSCRIPTEN_KEEPALIVE void add_bone_animation(
void *sceneManager,
EntityId asset,
int skinIndex,
int boneIndex,
const float *const frameData,
int numFrames,
float frameLengthInMs,
float fadeOutInSecs,
float fadeInInSecs,
float maxDelta)
{
((SceneManager *)sceneManager)->addBoneAnimation(asset, skinIndex, boneIndex, frameData, numFrames, frameLengthInMs, fadeOutInSecs, fadeInInSecs, maxDelta);
}
EMSCRIPTEN_KEEPALIVE void set_post_processing(void *const viewer, bool enabled)
{
((FilamentViewer *)viewer)->setPostProcessing(enabled);
}
EMSCRIPTEN_KEEPALIVE void set_antialiasing(void *const viewer, bool msaa, bool fxaa, bool taa)
{
((FilamentViewer *)viewer)->setAntiAliasing(msaa, fxaa, taa);
}
EMSCRIPTEN_KEEPALIVE EntityId get_bone(void *sceneManager,
EntityId entityId,
int skinIndex,
int boneIndex) {
return ((SceneManager*)sceneManager)->getBone(entityId, skinIndex, boneIndex);
}
EMSCRIPTEN_KEEPALIVE void get_world_transform(void *sceneManager,
EntityId entityId, float* const out) {
auto transform = ((SceneManager*)sceneManager)->getWorldTransform(entityId);
out[0] = transform[0][0];
out[1] = transform[0][1];
out[2] = transform[0][2];
out[3] = transform[0][3];
out[4] = transform[1][0];
out[5] = transform[1][1];
out[6] = transform[1][2];
out[7] = transform[1][3];
out[8] = transform[2][0];
out[9] = transform[2][1];
out[10] = transform[2][2];
out[11] = transform[2][3];
out[12] = transform[3][0];
out[13] = transform[3][1];
out[14] = transform[3][2];
out[15] = transform[3][3];
}
EMSCRIPTEN_KEEPALIVE void get_local_transform(void *sceneManager,
EntityId entityId, float* const out) {
auto transform = ((SceneManager*)sceneManager)->getLocalTransform(entityId);
out[0] = transform[0][0];
out[1] = transform[0][1];
out[2] = transform[0][2];
out[3] = transform[0][3];
out[4] = transform[1][0];
out[5] = transform[1][1];
out[6] = transform[1][2];
out[7] = transform[1][3];
out[8] = transform[2][0];
out[9] = transform[2][1];
out[10] = transform[2][2];
out[11] = transform[2][3];
out[12] = transform[3][0];
out[13] = transform[3][1];
out[14] = transform[3][2];
out[15] = transform[3][3];
}
EMSCRIPTEN_KEEPALIVE void get_rest_local_transforms(void *sceneManager,
EntityId entityId, int skinIndex, float* const out, int numBones) {
const auto transforms = ((SceneManager*)sceneManager)->getBoneRestTranforms(entityId, skinIndex);
auto numTransforms = transforms->size();
if(numTransforms != numBones) {
Log("Error - %d bone transforms available but you only specified %d.", numTransforms, numBones);
return;
}
for(int boneIndex = 0; boneIndex < numTransforms; boneIndex++) {
const auto transform = transforms->at(boneIndex);
for(int colNum = 0; colNum < 4; colNum++) {
for(int rowNum = 0; rowNum < 4; rowNum++) {
out[(boneIndex * 16) + (colNum * 4) + rowNum] = transform[colNum][rowNum];
}
}
}
}
EMSCRIPTEN_KEEPALIVE void get_inverse_bind_matrix(void *sceneManager,
EntityId entityId, int skinIndex, int boneIndex, float* const out) {
auto transform = ((SceneManager*)sceneManager)->getInverseBindMatrix(entityId, skinIndex, boneIndex);
out[0] = transform[0][0];
out[1] = transform[0][1];
out[2] = transform[0][2];
out[3] = transform[0][3];
out[4] = transform[1][0];
out[5] = transform[1][1];
out[6] = transform[1][2];
out[7] = transform[1][3];
out[8] = transform[2][0];
out[9] = transform[2][1];
out[10] = transform[2][2];
out[11] = transform[2][3];
out[12] = transform[3][0];
out[13] = transform[3][1];
out[14] = transform[3][2];
out[15] = transform[3][3];
}
EMSCRIPTEN_KEEPALIVE bool set_bone_transform(
void *sceneManager,
EntityId entityId,
int skinIndex,
int boneIndex,
const float *const transform)
{
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, skinIndex, boneIndex, matrix);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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();
}
EMSCRIPTEN_KEEPALIVE 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());
}
EMSCRIPTEN_KEEPALIVE int get_bone_count(void *sceneManager, EntityId assetEntity, int skinIndex) {
auto names = ((SceneManager *)sceneManager)->getBoneNames(assetEntity, skinIndex);
return names->size();
}
EMSCRIPTEN_KEEPALIVE void get_bone_names(void *sceneManager, EntityId assetEntity, const char** out, int skinIndex) {
auto names = ((SceneManager *)sceneManager)->getBoneNames(assetEntity, skinIndex);
for(int i = 0; i < names->size(); i++) {
auto name_c = names->at(i).c_str();
memcpy((void*)out[i], name_c, strlen(name_c) + 1);
}
}
EMSCRIPTEN_KEEPALIVE bool set_transform(void* sceneManager, EntityId entityId, const float* const transform) {
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)->setTransform(entityId, matrix);
}
EMSCRIPTEN_KEEPALIVE bool update_bone_matrices(void* sceneManager, EntityId entityId) {
return ((SceneManager*)sceneManager)->updateBoneMatrices(entityId);
}
EMSCRIPTEN_KEEPALIVE int get_morph_target_name_count(void *sceneManager, EntityId assetEntity, EntityId childEntity)
{
auto names = ((SceneManager *)sceneManager)->getMorphTargetNames(assetEntity, childEntity);
return (int)names->size();
}
EMSCRIPTEN_KEEPALIVE void get_morph_target_name(void *sceneManager, EntityId assetEntity, EntityId childEntity, char *const outPtr, int index)
{
auto names = ((SceneManager *)sceneManager)->getMorphTargetNames(assetEntity, childEntity);
std::string name = names->at(index);
strcpy(outPtr, name.c_str());
}
EMSCRIPTEN_KEEPALIVE void remove_entity(const void *const viewer, EntityId asset)
{
((FilamentViewer *)viewer)->removeEntity(asset);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void transform_to_unit_cube(void *sceneManager, EntityId asset)
{
((SceneManager *)sceneManager)->transformToUnitCube(asset);
}
EMSCRIPTEN_KEEPALIVE void set_position(void *sceneManager, EntityId asset, float x, float y, float z)
{
((SceneManager *)sceneManager)->setPosition(asset, x, y, z);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void set_scale(void *sceneManager, EntityId asset, float scale)
{
((SceneManager *)sceneManager)->setScale(asset, scale);
}
EMSCRIPTEN_KEEPALIVE void queue_position_update(void *sceneManager, EntityId asset, float x, float y, float z, bool relative)
{
((SceneManager *)sceneManager)->queuePositionUpdate(asset, x, y, z, relative);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void stop_animation(void *sceneManager, EntityId asset, int index)
{
((SceneManager *)sceneManager)->stopAnimation(asset, index);
}
EMSCRIPTEN_KEEPALIVE int hide_mesh(void *sceneManager, EntityId asset, const char *meshName)
{
return ((SceneManager *)sceneManager)->hide(asset, meshName);
}
EMSCRIPTEN_KEEPALIVE int reveal_mesh(void *sceneManager, EntityId asset, const char *meshName)
{
return ((SceneManager *)sceneManager)->reveal(asset, meshName);
}
EMSCRIPTEN_KEEPALIVE void filament_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);
}
EMSCRIPTEN_KEEPALIVE const char *get_name_for_entity(void *const sceneManager, const EntityId entityId)
{
return ((SceneManager *)sceneManager)->getNameForEntity(entityId);
}
EMSCRIPTEN_KEEPALIVE int get_entity_count(void *const sceneManager, const EntityId target, bool renderableOnly)
{
return ((SceneManager *)sceneManager)->getEntityCount(target, renderableOnly);
}
EMSCRIPTEN_KEEPALIVE void get_entities(void *const sceneManager, const EntityId target, bool renderableOnly, EntityId *out)
{
((SceneManager *)sceneManager)->getEntities(target, renderableOnly, out);
}
EMSCRIPTEN_KEEPALIVE const char *get_entity_name_at(void *const sceneManager, const EntityId target, int index, bool renderableOnly)
{
return ((SceneManager *)sceneManager)->getEntityNameAt(target, index, renderableOnly);
}
EMSCRIPTEN_KEEPALIVE void set_recording(void *const viewer, bool recording)
{
((FilamentViewer *)viewer)->setRecording(recording);
}
EMSCRIPTEN_KEEPALIVE void set_recording_output_directory(void *const viewer, const char *outputDirectory)
{
((FilamentViewer *)viewer)->setRecordingOutputDirectory(outputDirectory);
}
EMSCRIPTEN_KEEPALIVE void ios_dummy()
{
Log("Dummy called");
}
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr)
{
free(ptr);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void remove_collision_component(void *const sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->removeCollisionComponent(entityId);
}
EMSCRIPTEN_KEEPALIVE bool add_animation_component(void *const sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->addAnimationComponent(entityId);
}
EMSCRIPTEN_KEEPALIVE void remove_animation_component(void *const sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->removeAnimationComponent(entityId);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE EntityId get_parent(void *const sceneManager, EntityId child)
{
return ((SceneManager *)sceneManager)->getParent(child);
}
EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent)
{
((SceneManager *)sceneManager)->setParent(child, parent);
}
EMSCRIPTEN_KEEPALIVE void test_collisions(void *const sceneManager, EntityId entity)
{
((SceneManager *)sceneManager)->testCollisions(entity);
}
EMSCRIPTEN_KEEPALIVE void set_priority(void *const sceneManager, EntityId entity, int priority)
{
((SceneManager *)sceneManager)->setPriority(entity, priority);
}
EMSCRIPTEN_KEEPALIVE void get_gizmo(void *const sceneManager, EntityId *out)
{
return ((SceneManager *)sceneManager)->getGizmo(out);
}
}

View File

@@ -0,0 +1,876 @@
#ifdef __EMSCRIPTEN__
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
extern "C"
{
extern EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_WEBGL_CONTEXT_HANDLE thermion_dart_web_create_gl_context();
}
#include <pthread.h>
#endif
#include "ThermionDartFFIApi.h"
#include "FilamentViewer.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "filament/LightManager.h"
#include <functional>
#include <mutex>
#include <thread>
#include <stdlib.h>
using namespace thermion_flutter;
using namespace std::chrono_literals;
#include <time.h>
class RenderLoop
{
public:
explicit RenderLoop()
{
srand(time(NULL));
pthread_attr_t attr;
pthread_attr_init(&attr);
#ifdef __EMSCRIPTEN__
emscripten_pthread_attr_settransferredcanvases(&attr, "canvas");
#endif
pthread_create(&t, &attr, &RenderLoop::startHelper, this);
}
~RenderLoop()
{
_stop = true;
pthread_join(t, NULL);
}
static void mainLoop(void* arg) {
((RenderLoop*)arg)->iter();
}
static void *startHelper(void * parm) {
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop_arg(&RenderLoop::mainLoop, parm, 0, true);
#else
((RenderLoop*)parm)->start();
#endif
return nullptr;
}
void start() {
while (!_stop) {
iter();
}
}
void iter() {
auto frameStart = std::chrono::high_resolution_clock::now();
if (_rendering) {
doRender();
}
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(now - frameStart).count();
std::function<void()> task;
std::unique_lock<std::mutex> lock(_access);
while(true) {
now = std::chrono::high_resolution_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::microseconds>(now - frameStart).count();
if(elapsed >= _frameIntervalInMicroseconds) {
break;
}
if(!_tasks.empty()) {
task = std::move(_tasks.front());
_tasks.pop_front();
task();
} else {
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
}
}
}
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
{
FilamentViewer* viewer = nullptr;
#ifdef __EMSCRIPTEN__
_context = thermion_dart_web_create_gl_context();
auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)_context);
if(success != EMSCRIPTEN_RESULT_SUCCESS) {
std::cout << "Failed to make context current." << std::endl;
return viewer;
}
glClearColor(0.0, 0.5, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// emscripten_webgl_commit_frame();
viewer = (FilamentViewer*) create_filament_viewer((void* const) _context, loader, platform, uberArchivePath);
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0, $1);
}, callback, viewer);
#else
viewer = (FilamentViewer*)create_filament_viewer(context, loader, platform, uberArchivePath);
callback(viewer);
#endif
_viewer = viewer;
return viewer; });
auto fut = add_task(lambda);
}
void destroyViewer(FilamentViewer* viewer)
{
std::packaged_task<void()> lambda([=]() mutable
{
_rendering = false;
destroy_filament_viewer(viewer);
});
auto fut = add_task(lambda);
}
void setRendering(bool rendering, void(*callback)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
this->_rendering = rendering;
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = add_task(lambda);
}
void doRender()
{
#ifdef __EMSCRIPTEN__
if(emscripten_is_webgl_context_lost(_context) == EM_TRUE) {
Log("Context lost");
auto sleepFor = std::chrono::seconds(1);
std::this_thread::sleep_for(sleepFor);
return;
}
#endif
render(_viewer, 0, nullptr, nullptr, nullptr);
if (_renderCallback)
{
_renderCallback(_renderCallbackOwner);
}
#ifdef __EMSCRIPTEN__
// emscripten_webgl_commit_frame();
#endif
}
void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds)
{
_frameIntervalInMicroseconds = static_cast<int>(1000.0f * 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;
}
bool _stop = false;
bool _rendering = false;
int _frameIntervalInMicroseconds = 1000000.0 / 60.0;
std::mutex _access;
void (*_renderCallback)(void *const) = nullptr;
void *_renderCallbackOwner = nullptr;
pthread_t t;
std::condition_variable _cond;
std::deque<std::function<void()>> _tasks;
FilamentViewer* _viewer = nullptr;
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE _context;
int _frameNum = 0;
#endif
};
extern "C"
{
static RenderLoop *_rl;
EMSCRIPTEN_KEEPALIVE void create_filament_viewer_ffi(
void *const context, void *const platform, const char *uberArchivePath,
const void *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);
}
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_ffi(void *const viewer)
{
_rl->destroyViewer((FilamentViewer*)viewer);
}
EMSCRIPTEN_KEEPALIVE 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 %lu & surface %lu", width, height, viewer, surface);
std::packaged_task<void()> lambda(
[=]() mutable
{
create_swap_chain(viewer, surface, width, height);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, onComplete);
#else
onComplete();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)())
{
Log("Destroying swapchain");
std::packaged_task<void()> lambda(
[=]() mutable
{
destroy_swap_chain(viewer);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, onComplete);
#else
onComplete();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, onComplete);
#else
onComplete();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, onComplete);
#else
onComplete();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void set_rendering_ffi(void *const viewer,
bool rendering, void (*callback)())
{
if (!_rl)
{
Log("No render loop!"); // PANIC?
}
else
{
_rl->setRendering(rendering, callback);
if (rendering)
{
Log("Set rendering to true");
}
else
{
Log("Set rendering to false");
}
}
}
EMSCRIPTEN_KEEPALIVE void
set_frame_interval_ffi(void* const viewer, float frameIntervalInMilliseconds)
{
_rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds);
std::packaged_task<void()> lambda([=]() mutable
{ ((FilamentViewer*)viewer)->setFrameInterval(frameIntervalInMilliseconds); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void render_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]() mutable
{ _rl->doRender(); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0, $1);
}, callback, entity);
#else
callback(entity);
#endif
return entity; });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0, $1);
}, callback, entity);
#else
callback(entity);
#endif
return entity;
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void clear_background_image_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ clear_background_image(viewer); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void set_bloom_ffi(void *const viewer, float strength)
{
std::packaged_task<void()> lambda([=]
{ set_bloom(viewer, strength); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void load_skybox_ffi(void *const viewer,
const char *skyboxPath,
void (*onComplete)())
{
std::packaged_task<void()> lambda([=]
{
load_skybox(viewer, skyboxPath);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, onComplete);
#else
onComplete();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE void remove_skybox_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ remove_skybox(viewer); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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,
float falloffRadius,
float spotLightConeInner,
float spotLightConeOuter,
float sunAngularRadius,
float sunHaloSize,
float sunHaloFallof,
bool shadows,
void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda([=]
{
auto entity = add_light(
viewer,
type,
colour,
intensity,
posX,
posY,
posZ,
dirX,
dirY,
dirZ,
falloffRadius,
spotLightConeInner,
spotLightConeOuter,
sunAngularRadius,
sunHaloSize,
sunHaloFallof,
shadows);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0, $1);
}, callback, entity);
#else
callback(entity);
#endif
return entity; });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void remove_light_ffi(void *const viewer,
EntityId entityId)
{
std::packaged_task<void()> lambda([=]
{ remove_light(viewer, entityId); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void clear_lights_ffi(void *const viewer)
{
std::packaged_task<void()> lambda([=]
{ clear_lights(viewer); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void remove_entity_ffi(void *const viewer,
EntityId asset, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
remove_entity(viewer, asset);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void clear_entities_ffi(void *const viewer, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
clear_entities(viewer);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, success);
#else
callback(success);
#endif
return success;
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
get_morph_target_name_ffi(void *sceneManager, EntityId assetEntity,
EntityId childEntity, char *const outPtr, int index, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
get_morph_target_name(sceneManager, assetEntity, childEntity, outPtr, index);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
get_morph_target_name_count_ffi(void *sceneManager, EntityId assetEntity,
EntityId childEntity, void (*callback)(int))
{
std::packaged_task<int()> lambda([=]
{
auto count = get_morph_target_name_count(sceneManager, assetEntity, childEntity);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, count);
#else
callback(count);
#endif
return count; });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, count);
#else
callback(count);
#endif
return count;
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, name);
#else
callback(name);
#endif
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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, result);
#else
callback(result);
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void set_bone_transform_ffi(
void *sceneManager,
EntityId asset,
int skinIndex,
int boneIndex,
const float *const transform,
void (*callback)(bool))
{
std::packaged_task<bool()> lambda(
[=]
{
auto success = set_bone_transform(sceneManager, asset, skinIndex, boneIndex, transform);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, success);
#else
callback(success);
#endif
return success;
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void update_bone_matrices_ffi(void *sceneManager,
EntityId entity, void(*callback)(bool)) {
std::packaged_task<void()> lambda(
[=]
{
auto success = update_bone_matrices(sceneManager, entity);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback, success);
#else
callback(success);
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId, void(*callback)())
{
std::packaged_task<void()> lambda(
[=]
{
reset_to_rest_pose(sceneManager, entityId);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0);
}, callback);
#else
callback();
#endif
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE 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);
#ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, entity);
#else
callback(entity);
#endif
return entity;
});
auto fut = _rl->add_task(lambda);
}
}

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