Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/API for controlling scene #15

Merged
merged 23 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_library(
../cpp/core/CameraWrapper.cpp
../cpp/core/ViewWrapper.cpp
../cpp/core/SwapChainWrapper.cpp
../cpp/core/FilamentAssetWrapper.cpp

# Filament Utils
../cpp/core/utils/EntityWrapper.cpp
Expand Down
35 changes: 35 additions & 0 deletions package/cpp/core/CameraFovEnum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Created by Marc Rousavy on 22.02.24.
//

#pragma once

#include "jsi/EnumMapper.h"
#include <filament/Camera.h>

namespace margelo {
using namespace filament;

namespace EnumMapper {
static void convertJSUnionToEnum(const std::string& inUnion, Camera::Fov* outEnum) {
if (inUnion == "horizontal")
*outEnum = Camera::Fov::HORIZONTAL;
else if (inUnion == "vertical")
*outEnum = Camera::Fov::VERTICAL;
else
throw invalidUnion(inUnion);
}
static void convertEnumToJSUnion(Camera::Fov inEnum, std::string* outUnion) {
switch (inEnum) {
case filament::Camera::Fov::HORIZONTAL:
*outUnion = "horizontal";
break;
case filament::Camera::Fov::VERTICAL:
*outUnion = "vertical";
break;
default:
throw invalidEnum(inEnum);
}
}
} // namespace EnumMapper
} // namespace margelo
26 changes: 25 additions & 1 deletion package/cpp/core/CameraWrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#include "CameraWrapper.h"
#include "CameraFovEnum.h"

void margelo::CameraWrapper::loadHybridMethods() {
registerHybridMethod("lookAtCameraManipulator", &CameraWrapper::lookAtCameraManipulator, this);
registerHybridMethod("lookAt", &CameraWrapper::lookAt, this);
registerHybridMethod("setLensProjection", &CameraWrapper::setLensProjection, this);
registerHybridMethod("setProjection", &CameraWrapper::setProjection, this);
}

void margelo::CameraWrapper::lookAt(std::shared_ptr<ManipulatorWrapper> cameraManipulator) {
void margelo::CameraWrapper::lookAtCameraManipulator(std::shared_ptr<ManipulatorWrapper> cameraManipulator) {
if (!cameraManipulator) {
throw std::invalid_argument("CameraManipulator is null");
}
Expand All @@ -13,3 +17,23 @@ void margelo::CameraWrapper::lookAt(std::shared_ptr<ManipulatorWrapper> cameraMa
cameraManipulator->getManipulator()->getLookAt(&eye, &center, &up);
_camera->lookAt(eye, center, up);
}

void margelo::CameraWrapper::lookAt(std::vector<double> eye, std::vector<double> center, std::vector<double> up) {
math::float3 eyeVec = {static_cast<float>(eye[0]), static_cast<float>(eye[1]), static_cast<float>(eye[2])};
math::float3 centerVec = {static_cast<float>(center[0]), static_cast<float>(center[1]), static_cast<float>(center[2])};
math::float3 upVec = {static_cast<float>(up[0]), static_cast<float>(up[1]), static_cast<float>(up[2])};
_camera->lookAt(eyeVec, centerVec, upVec);
}

void margelo::CameraWrapper::setLensProjection(double fov, double aspect, double near, double far) {
_camera->setLensProjection(static_cast<float>(fov), static_cast<float>(aspect), static_cast<float>(near), static_cast<float>(far));
}

void margelo::CameraWrapper::setProjection(double fovInDegrees, double aspect, double near, double far,
std::string directionStr = "vertical") {
Camera::Fov direction;
EnumMapper::convertJSUnionToEnum(directionStr, &direction);

_camera->setProjection(static_cast<float>(fovInDegrees), static_cast<float>(aspect), static_cast<float>(near), static_cast<float>(far),
direction);
}
6 changes: 5 additions & 1 deletion package/cpp/core/CameraWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ class CameraWrapper : public HybridObject {
std::shared_ptr<Camera> _camera;

private:
void lookAt(std::vector<double> eye, std::vector<double> center, std::vector<double> up);
void setLensProjection(double fov, double aspect, double near, double far);
// TODO(Hanno): Add directionStr , Camera::Fov directionStr = Camera::Fov::VERTICAL
void setProjection(double fovInDegrees, double aspect, double near, double far, std::string directionStr);
// Convenience methods
void lookAt(std::shared_ptr<ManipulatorWrapper> cameraManipulator);
void lookAtCameraManipulator(std::shared_ptr<ManipulatorWrapper> cameraManipulator);
};
} // namespace margelo
110 changes: 72 additions & 38 deletions package/cpp/core/EngineWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

#include "EngineWrapper.h"

#include "LightEnum.h"
#include "References.h"
#include "utils/Converter.h"

#include <filament/Color.h>
#include <filament/Engine.h>
#include <filament/Fence.h>
Expand Down Expand Up @@ -78,7 +81,7 @@ EngineWrapper::EngineWrapper(std::shared_ptr<Choreographer> choreographer) {
void EngineWrapper::loadHybridMethods() {
registerHybridMethod("setSurfaceProvider", &EngineWrapper::setSurfaceProvider, this);
registerHybridMethod("setRenderCallback", &EngineWrapper::setRenderCallback, this);
registerHybridMethod("createDefaultLight", &EngineWrapper::createDefaultLight, this);
registerHybridMethod("setIndirectLight", &EngineWrapper::setIndirectLight, this);

registerHybridMethod("loadAsset", &EngineWrapper::loadAsset, this);

Expand All @@ -88,6 +91,11 @@ void EngineWrapper::loadHybridMethods() {
registerHybridMethod("getView", &EngineWrapper::getView, this);
registerHybridMethod("getCamera", &EngineWrapper::getCamera, this);
registerHybridMethod("getCameraManipulator", &EngineWrapper::getCameraManipulator, this);
registerHybridMethod("createLightEntity", &EngineWrapper::createLightEntity, this);
registerHybridMethod("transformToUnitCube", &EngineWrapper::transformToUnitCube, this);
registerHybridMethod("setEntityPosition", &EngineWrapper::setEntityPosition, this);
registerHybridMethod("setEntityRotation", &EngineWrapper::setEntityRotation, this);
registerHybridMethod("setEntityScale", &EngineWrapper::setEntityScale, this);
}

void EngineWrapper::setSurfaceProvider(std::shared_ptr<SurfaceProvider> surfaceProvider) {
Expand Down Expand Up @@ -132,7 +140,8 @@ void EngineWrapper::surfaceSizeChanged(int width, int height) {
_view->setViewport(0, 0, width, height);
}

updateCameraProjection();
// TODO: when the surface resizes we need to update the camera projection, but that one is owned by JS now.
// updateCameraProjection();
}

void EngineWrapper::destroySurface() {
Expand Down Expand Up @@ -223,8 +232,8 @@ std::shared_ptr<CameraWrapper> EngineWrapper::createCamera() {
return std::make_shared<CameraWrapper>(camera);
}

void EngineWrapper::loadAsset(std::shared_ptr<FilamentBuffer> modelBuffer) {
filament::gltfio::FilamentAsset* asset = _assetLoader->createAsset(modelBuffer->getData(), modelBuffer->getSize());
std::shared_ptr<FilamentAssetWrapper> EngineWrapper::loadAsset(std::shared_ptr<FilamentBuffer> modelBuffer) {
gltfio::FilamentAsset* asset = _assetLoader->createAsset(modelBuffer->getData(), modelBuffer->getSize());
if (asset == nullptr) {
throw std::runtime_error("Failed to load asset");
}
Expand All @@ -242,11 +251,14 @@ void EngineWrapper::loadAsset(std::shared_ptr<FilamentBuffer> modelBuffer) {
_animator = asset->getInstance()->getAnimator();
asset->releaseSourceData();

transformToUnitCube(asset);
auto sharedPtr = std::shared_ptr<gltfio::FilamentAsset>(asset, [](gltfio::FilamentAsset* asset) {
// TODO: destroy the asset
});
return std::make_shared<FilamentAssetWrapper>(sharedPtr);
}

// Default light is a directional light for shadows + a default IBL
void EngineWrapper::createDefaultLight(std::shared_ptr<FilamentBuffer> iblBuffer) {
void EngineWrapper::setIndirectLight(std::shared_ptr<FilamentBuffer> iblBuffer) {
if (!_scene) {
throw std::runtime_error("Scene not initialized");
}
Expand Down Expand Up @@ -274,17 +286,23 @@ void EngineWrapper::createDefaultLight(std::shared_ptr<FilamentBuffer> iblBuffer
IndirectLight::Builder().reflections(cubemap).irradiance(3, harmonics).intensity(30000.0f).build(*_engine);

_scene->getScene()->setIndirectLight(_indirectLight);
}

// Add directional light for supporting shadows
std::shared_ptr<EntityWrapper> EngineWrapper::createLightEntity(std::string lightTypeStr, double colorFahrenheit, double intensity,
double directionX, double directionY, double directionZ, bool castShadows) {
auto lightEntity = _engine->getEntityManager().create();
LightManager::Builder(LightManager::Type::DIRECTIONAL)
.color(Color::cct(6500.0f))
.intensity(10000)
.direction({0, -1, 0})
.castShadows(true)
.build(*_engine, lightEntity);

_scene->getScene()->addEntity(lightEntity);
// TODO(Marc): Fix enum converter
LightManager::Type lightType;
EnumMapper::convertJSUnionToEnum(lightTypeStr, &lightType);

LightManager::Builder(lightType)
.color(Color::cct(static_cast<float>(colorFahrenheit)))
.intensity(static_cast<float>(intensity))
.direction({directionX, directionY, directionZ})
.castShadows(castShadows)
.build(*_engine, lightEntity);
return std::make_shared<EntityWrapper>(lightEntity);
}

std::shared_ptr<ManipulatorWrapper> EngineWrapper::createCameraManipulator(int width, int height) {
Expand All @@ -301,31 +319,9 @@ std::shared_ptr<ManipulatorWrapper> EngineWrapper::createCameraManipulator(int w
/**
* Sets up a root transform on the current model to make it fit into a unit cube.
*/
void EngineWrapper::transformToUnitCube(filament::gltfio::FilamentAsset* asset) {
void EngineWrapper::transformToUnitCube(std::shared_ptr<FilamentAssetWrapper> asset) {
TransformManager& tm = _engine->getTransformManager();
Aabb aabb = asset->getBoundingBox();
math::details::TVec3<float> center = aabb.center();
math::details::TVec3<float> halfExtent = aabb.extent();
float maxExtent = max(halfExtent) * 2.0f;
float scaleFactor = 2.0f / maxExtent;
math::mat4f transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
EntityInstance<TransformManager> transformInstance = tm.getInstance(asset->getRoot());
tm.setTransform(transformInstance, transform);
}

void EngineWrapper::updateCameraProjection() {
if (!_view) {
throw std::runtime_error("View not initialized");
}
if (!_camera) {
throw std::runtime_error("Camera not initialized");
}

const double aspect = (double)_view->getView()->getViewport().width / _view->getView()->getViewport().height;
double focalLength = 28.0;
double near = 0.05; // 5cm
double far = 1000.0; // 1km
_camera->getCamera()->setLensProjection(focalLength, aspect, near, far);
asset->transformToUnitCube(tm);
}

void EngineWrapper::synchronizePendingFrames() {
Expand All @@ -340,4 +336,42 @@ void EngineWrapper::synchronizePendingFrames() {
_engine->destroy(fence);
}

/**
* Internal method that will help updating the transform of an entity.
* @param transform The transform matrix to apply
* @param entity The entity to apply the transform to
* @param multiplyCurrent If true, the current transform will be multiplied with the new transform, otherwise it will be replaced
*/
void EngineWrapper::updateTransform(math::mat4 transform, std::shared_ptr<EntityWrapper> entity, bool multiplyCurrent) {
if (!entity) {
throw std::invalid_argument("Entity is null");
}

TransformManager& tm = _engine->getTransformManager();
EntityInstance<TransformManager> entityInstance = tm.getInstance(entity->getEntity());
auto currentTransform = tm.getTransform(entityInstance);
auto newTransform = multiplyCurrent ? (currentTransform * transform) : transform;
tm.setTransform(entityInstance, newTransform);
}

// TODO(Marc): Ideally i want to do this in the entity wrapper, but i dont have access to the transform manager there
void EngineWrapper::setEntityPosition(std::shared_ptr<EntityWrapper> entity, std::vector<double> positionVec, bool multiplyCurrent) {
math::float3 position = Converter::VecToFloat3(positionVec);
auto translationMatrix = math::mat4::translation(position);
updateTransform(translationMatrix, entity, multiplyCurrent);
}

void EngineWrapper::setEntityRotation(std::shared_ptr<EntityWrapper> entity, double angleRadians, std::vector<double> axisVec,
bool multiplyCurrent) {
math::float3 axis = Converter::VecToFloat3(axisVec);
auto rotationMatrix = math::mat4::rotation(angleRadians, axis);
updateTransform(rotationMatrix, entity, multiplyCurrent);
}

void EngineWrapper::setEntityScale(std::shared_ptr<EntityWrapper> entity, std::vector<double> scaleVec, bool multiplyCurrent) {
math::float3 scale = Converter::VecToFloat3(scaleVec);
auto scaleMatrix = math::mat4::scaling(scale);
updateTransform(scaleMatrix, entity, multiplyCurrent);
}

} // namespace margelo
34 changes: 28 additions & 6 deletions package/cpp/core/EngineWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,24 @@

#pragma once

#include "jsi/HybridObject.h"

#include "CameraWrapper.h"
#include "Choreographer.h"
#include "FilamentAssetWrapper.h"
#include "FilamentBuffer.h"
#include "RendererWrapper.h"
#include "SceneWrapper.h"
#include "Surface.h"
#include "SurfaceProvider.h"
#include <core/utils/EntityWrapper.h>
#include "SwapChainWrapper.h"
#include "ViewWrapper.h"
#include "core/utils/EntityWrapper.h"
#include "core/utils/ManipulatorWrapper.h"

#include <camutils/Manipulator.h>
#include <filament/Engine.h>
#include <filament/LightManager.h>
#include <filament/SwapChain.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/MaterialProvider.h>
Expand Down Expand Up @@ -48,12 +62,20 @@ class EngineWrapper : public HybridObject {
void setRenderCallback(std::function<void(std::shared_ptr<EngineWrapper>)> callback);
void renderFrame(double timestamp);

void transformToUnitCube(gltfio::FilamentAsset* asset);
void loadAsset(std::shared_ptr<FilamentBuffer> modelBuffer);
void createDefaultLight(std::shared_ptr<FilamentBuffer> modelBuffer);
void updateCameraProjection();
void transformToUnitCube(std::shared_ptr<FilamentAssetWrapper> asset);
std::shared_ptr<FilamentAssetWrapper> loadAsset(std::shared_ptr<FilamentBuffer> modelBuffer);
void setIndirectLight(std::shared_ptr<FilamentBuffer> modelBuffer);

void synchronizePendingFrames();

std::shared_ptr<EntityWrapper> createLightEntity(std::string lightTypeStr, double colorFahrenheit, double intensity, double directionX,
double directionY, double directionZ, bool castShadows);

void updateTransform(math::mat4 transform, std::shared_ptr<EntityWrapper> entity, bool multiplyCurrent);
void setEntityPosition(std::shared_ptr<EntityWrapper> entity, std::vector<double> positionVec, bool multiplyCurrent);
void setEntityRotation(std::shared_ptr<EntityWrapper> entity, double angleRadians, std::vector<double> axisVec, bool multiplyCurrent);
void setEntityScale(std::shared_ptr<EntityWrapper> entity, std::vector<double> scaleVec, bool multiplyCurrent);

private:
std::shared_ptr<Engine> _engine;
std::shared_ptr<SurfaceProvider> _surfaceProvider;
Expand All @@ -71,7 +93,7 @@ class EngineWrapper : public HybridObject {
std::shared_ptr<gltfio::ResourceLoader> _resourceLoader;

const math::float3 defaultObjectPosition = {0.0f, 0.0f, 0.0f};
const math::float3 defaultCameraPosition = {0.0f, 0.0f, 5.0f};
const math::float3 defaultCameraPosition = {0.0f, 0.0f, 0.0f};

private:
// Internals we create, but share the access with the user
Expand Down
33 changes: 33 additions & 0 deletions package/cpp/core/FilamentAssetWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "FilamentAssetWrapper.h"

#include <utils/Entity.h>
#include <utils/EntityInstance.h>

namespace margelo {

using namespace utils;

void FilamentAssetWrapper::loadHybridMethods() {
registerHybridMethod("getRoot", &FilamentAssetWrapper::getRoot, this);
}

/**
* Sets up a root transform on the current model to make it fit into a unit cube.
*/
void FilamentAssetWrapper::transformToUnitCube(TransformManager& transformManager) {
Aabb aabb = _asset->getBoundingBox();
math::details::TVec3<float> center = aabb.center();
math::details::TVec3<float> halfExtent = aabb.extent();
float maxExtent = max(halfExtent) * 2.0f;
float scaleFactor = 2.0f / maxExtent;
math::mat4f transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
EntityInstance<TransformManager> transformInstance = transformManager.getInstance(_asset->getRoot());
transformManager.setTransform(transformInstance, transform);
}

std::shared_ptr<EntityWrapper> FilamentAssetWrapper::getRoot() {
Entity rootEntity = _asset->getRoot();
return std::make_shared<EntityWrapper>(rootEntity);
}

} // namespace margelo
Loading