camera fixes for assets with large bounding boxes
This commit is contained in:
@@ -177,7 +177,7 @@ namespace polyvox {
|
|||||||
void loadPngTexture(string path, ResourceBuffer data);
|
void loadPngTexture(string path, ResourceBuffer data);
|
||||||
void loadTextureFromPath(string path);
|
void loadTextureFromPath(string path);
|
||||||
|
|
||||||
|
Manipulator<double>* _manipulator = nullptr;
|
||||||
void _createManipulator();
|
void _createManipulator();
|
||||||
uint32_t _lastFrameTimeInNanos;
|
uint32_t _lastFrameTimeInNanos;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ A new flutter plugin project.
|
|||||||
s.license = { :file => '../LICENSE' }
|
s.license = { :file => '../LICENSE' }
|
||||||
s.author = { 'Your Company' => 'email@example.com' }
|
s.author = { 'Your Company' => 'email@example.com' }
|
||||||
s.source = { :path => '.' }
|
s.source = { :path => '.' }
|
||||||
s.source_files = 'Classes/*', 'src/*', 'src/ios/*', 'include/filament/*', 'include/*', 'include/material/*.c'
|
s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'src/ios/*', 'include/filament/*', 'include/*', 'include/material/*.c'
|
||||||
s.public_header_files = 'include/SwiftPolyvoxFilamentPlugin-Bridging-Header.h', 'include/PolyvoxFilamentApi.h', 'include/PolyvoxFilamentFFIApi.h', 'include/ResourceBuffer.hpp', 'include/Log.hpp'
|
s.public_header_files = 'include/SwiftPolyvoxFilamentPlugin-Bridging-Header.h', 'include/PolyvoxFilamentApi.h', 'include/PolyvoxFilamentFFIApi.h', 'include/ResourceBuffer.hpp', 'include/Log.hpp'
|
||||||
s.dependency 'Flutter'
|
s.dependency 'Flutter'
|
||||||
s.platform = :ios, '12.1'
|
s.platform = :ios, '12.1'
|
||||||
@@ -26,7 +26,7 @@ A new flutter plugin project.
|
|||||||
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
||||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include/filament" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/shaders" "$(inherited)"',
|
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include/filament" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/shaders" "$(inherited)"',
|
||||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
||||||
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
|
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ A new flutter plugin project.
|
|||||||
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
||||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include/filament" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/shaders" "$(inherited)"',
|
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/include/filament" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/image" "${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/src/shaders" "$(inherited)"',
|
||||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lpng16 -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lstdc++',
|
||||||
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
|
'LIBRARY_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/polyvox_filament/ios/lib" "$(inherited)"',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include <filament/Camera.h>
|
#include <filament/Camera.h>
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/platforms/OpenGLPlatform.h>
|
#include <backend/platforms/OpenGLPlatform.h>
|
||||||
#include <filament/ColorGrading.h>
|
#include <filament/ColorGrading.h>
|
||||||
@@ -86,12 +87,14 @@ using namespace gltfio;
|
|||||||
using namespace utils;
|
using namespace utils;
|
||||||
using namespace image;
|
using namespace image;
|
||||||
|
|
||||||
namespace filament {
|
namespace filament
|
||||||
|
{
|
||||||
class IndirectLight;
|
class IndirectLight;
|
||||||
class LightManager;
|
class LightManager;
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|
||||||
namespace polyvox {
|
namespace polyvox
|
||||||
|
{
|
||||||
|
|
||||||
const double kNearPlane = 0.05; // 5 cm
|
const double kNearPlane = 0.05; // 5 cm
|
||||||
const double kFarPlane = 1000.0; // 1 km
|
const double kFarPlane = 1000.0; // 1 km
|
||||||
@@ -99,7 +102,8 @@ const double kFarPlane = 1000.0; // 1 km
|
|||||||
// const float kAperture = 1.0f;
|
// const float kAperture = 1.0f;
|
||||||
// const float kShutterSpeed = 1.0f;
|
// const float kShutterSpeed = 1.0f;
|
||||||
// const float kSensitivity = 50.0f;
|
// const float kSensitivity = 50.0f;
|
||||||
struct Vertex {
|
struct Vertex
|
||||||
|
{
|
||||||
filament::math::float2 position;
|
filament::math::float2 position;
|
||||||
uint32_t color;
|
uint32_t color;
|
||||||
};
|
};
|
||||||
@@ -112,7 +116,8 @@ static constexpr float4 sFullScreenTriangleVertices[3] = {
|
|||||||
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
|
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
|
||||||
|
|
||||||
FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapper *const resourceLoaderWrapper, void *const platform, const char *uberArchivePath)
|
FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapper *const resourceLoaderWrapper, void *const platform, const char *uberArchivePath)
|
||||||
: _resourceLoaderWrapper(resourceLoaderWrapper) {
|
: _resourceLoaderWrapper(resourceLoaderWrapper)
|
||||||
|
{
|
||||||
|
|
||||||
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
||||||
|
|
||||||
@@ -193,8 +198,7 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
_ncm,
|
_ncm,
|
||||||
_engine,
|
_engine,
|
||||||
_scene,
|
_scene,
|
||||||
uberArchivePath
|
uberArchivePath);
|
||||||
);
|
|
||||||
|
|
||||||
_imageTexture = Texture::Builder()
|
_imageTexture = Texture::Builder()
|
||||||
.width(1)
|
.width(1)
|
||||||
@@ -203,7 +207,8 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
.format(Texture::InternalFormat::RGB16F)
|
.format(Texture::InternalFormat::RGB16F)
|
||||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||||
.build(*_engine);
|
.build(*_engine);
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
_imageMaterial =
|
_imageMaterial =
|
||||||
Material::Builder()
|
Material::Builder()
|
||||||
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
|
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
|
||||||
@@ -211,7 +216,9 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||||
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f));
|
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||||
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
||||||
} catch(...) {
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
Log("Failed to load background image material provider");
|
Log("Failed to load background image material provider");
|
||||||
std::rethrow_exception(std::current_exception());
|
std::rethrow_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
@@ -250,21 +257,25 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
_scene->addEntity(imageEntity);
|
_scene->addEntity(imageEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setPostProcessing(bool enabled) {
|
void FilamentViewer::setPostProcessing(bool enabled)
|
||||||
|
{
|
||||||
_view->setPostProcessingEnabled(enabled);
|
_view->setPostProcessingEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setBloom(float strength) {
|
void FilamentViewer::setBloom(float strength)
|
||||||
|
{
|
||||||
decltype(_view->getBloomOptions()) opts;
|
decltype(_view->getBloomOptions()) opts;
|
||||||
opts.enabled = true;
|
opts.enabled = true;
|
||||||
opts.strength = strength;
|
opts.strength = strength;
|
||||||
_view->setBloomOptions(opts);
|
_view->setBloomOptions(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setToneMapping(ToneMapping toneMapping) {
|
void FilamentViewer::setToneMapping(ToneMapping toneMapping)
|
||||||
|
{
|
||||||
|
|
||||||
ToneMapper *tm;
|
ToneMapper *tm;
|
||||||
switch(toneMapping) {
|
switch (toneMapping)
|
||||||
|
{
|
||||||
case ToneMapping::ACES:
|
case ToneMapping::ACES:
|
||||||
Log("Setting tone mapping to ACES");
|
Log("Setting tone mapping to ACES");
|
||||||
tm = new ACESToneMapper();
|
tm = new ACESToneMapper();
|
||||||
@@ -282,21 +293,22 @@ void FilamentViewer::setToneMapping(ToneMapping toneMapping) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto newColorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine);
|
auto newColorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine);
|
||||||
_view->setColorGrading(newColorGrading);
|
_view->setColorGrading(newColorGrading);
|
||||||
_engine->destroy(colorGrading);
|
_engine->destroy(colorGrading);
|
||||||
delete tm;
|
delete tm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setFrameInterval(float frameInterval) {
|
void FilamentViewer::setFrameInterval(float frameInterval)
|
||||||
|
{
|
||||||
Renderer::FrameRateOptions fro;
|
Renderer::FrameRateOptions fro;
|
||||||
fro.interval = frameInterval;
|
fro.interval = frameInterval;
|
||||||
_renderer->setFrameRateOptions(fro);
|
_renderer->setFrameRateOptions(fro);
|
||||||
Log("Set framerate interval to %f", frameInterval);
|
Log("Set framerate interval to %f", frameInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) {
|
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows)
|
||||||
|
{
|
||||||
auto light = EntityManager::get().create();
|
auto light = EntityManager::get().create();
|
||||||
LightManager::Builder(t)
|
LightManager::Builder(t)
|
||||||
.color(Color::cct(colour))
|
.color(Color::cct(colour))
|
||||||
@@ -312,30 +324,37 @@ int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float inten
|
|||||||
return entityId;
|
return entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeLight(EntityId entityId) {
|
void FilamentViewer::removeLight(EntityId entityId)
|
||||||
|
{
|
||||||
Log("Removing light with entity ID %d", entityId);
|
Log("Removing light with entity ID %d", entityId);
|
||||||
auto entity = utils::Entity::import(entityId);
|
auto entity = utils::Entity::import(entityId);
|
||||||
if(entity.isNull()) {
|
if (entity.isNull())
|
||||||
|
{
|
||||||
Log("Error: light entity not found under ID %d", entityId);
|
Log("Error: light entity not found under ID %d", entityId);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
remove(_lights.begin(), _lights.end(), entity);
|
remove(_lights.begin(), _lights.end(), entity);
|
||||||
_scene->remove(entity);
|
_scene->remove(entity);
|
||||||
EntityManager::get().destroy(1, &entity);
|
EntityManager::get().destroy(1, &entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::clearLights() {
|
void FilamentViewer::clearLights()
|
||||||
|
{
|
||||||
Log("Removing all lights");
|
Log("Removing all lights");
|
||||||
_scene->removeEntities(_lights.data(), _lights.size());
|
_scene->removeEntities(_lights.data(), _lights.size());
|
||||||
EntityManager::get().destroy(_lights.size(), _lights.data());
|
EntityManager::get().destroy(_lights.size(), _lights.data());
|
||||||
_lights.clear();
|
_lights.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool endsWith(string path, string ending) {
|
static bool endsWith(string path, string ending)
|
||||||
|
{
|
||||||
return path.compare(path.length() - ending.length(), ending.length(), ending) == 0;
|
return path.compare(path.length() - ending.length(), ending.length(), ending) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadKtx2Texture(string path, ResourceBuffer rb) {
|
void FilamentViewer::loadKtx2Texture(string path, ResourceBuffer rb)
|
||||||
|
{
|
||||||
|
|
||||||
// TODO - check all this
|
// TODO - check all this
|
||||||
|
|
||||||
@@ -356,34 +375,36 @@ void FilamentViewer::loadKtx2Texture(string path, ResourceBuffer rb) {
|
|||||||
// ktxreader::Ktx2Reader::TransferFunction::LINEAR);
|
// ktxreader::Ktx2Reader::TransferFunction::LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadKtxTexture(string path, ResourceBuffer rb) {
|
void FilamentViewer::loadKtxTexture(string path, ResourceBuffer rb)
|
||||||
|
{
|
||||||
ktxreader::Ktx1Bundle *bundle =
|
ktxreader::Ktx1Bundle *bundle =
|
||||||
new ktxreader::Ktx1Bundle(static_cast<const uint8_t *>(rb.data),
|
new ktxreader::Ktx1Bundle(static_cast<const uint8_t *>(rb.data),
|
||||||
static_cast<uint32_t>(rb.size));
|
static_cast<uint32_t>(rb.size));
|
||||||
|
|
||||||
|
|
||||||
// because the ResourceBuffer will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
// because the ResourceBuffer will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
||||||
ResourceBuffer *rbCopy = new ResourceBuffer(rb);
|
ResourceBuffer *rbCopy = new ResourceBuffer(rb);
|
||||||
|
|
||||||
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, rbCopy};
|
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, rbCopy};
|
||||||
|
|
||||||
|
|
||||||
_imageTexture =
|
_imageTexture =
|
||||||
ktxreader::Ktx1Reader::createTexture(_engine, *bundle, false, [](void* userdata) {
|
ktxreader::Ktx1Reader::createTexture(
|
||||||
|
_engine, *bundle, false, [](void *userdata)
|
||||||
|
{
|
||||||
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
||||||
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
||||||
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
||||||
loader->free(*rb);
|
loader->free(*rb);
|
||||||
delete rb;
|
delete rb;
|
||||||
delete vec;
|
delete vec; },
|
||||||
}, callbackData);
|
callbackData);
|
||||||
|
|
||||||
auto info = bundle->getInfo();
|
auto info = bundle->getInfo();
|
||||||
_imageWidth = info.pixelWidth;
|
_imageWidth = info.pixelWidth;
|
||||||
_imageHeight = info.pixelHeight;
|
_imageHeight = info.pixelHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb)
|
||||||
|
{
|
||||||
|
|
||||||
polyvox::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size);
|
polyvox::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size);
|
||||||
|
|
||||||
@@ -392,7 +413,8 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
|||||||
LinearImage *image = new LinearImage(ImageDecoder::decode(
|
LinearImage *image = new LinearImage(ImageDecoder::decode(
|
||||||
inputStream, path.c_str(), ImageDecoder::ColorSpace::SRGB));
|
inputStream, path.c_str(), ImageDecoder::ColorSpace::SRGB));
|
||||||
|
|
||||||
if (!image->isValid()) {
|
if (!image->isValid())
|
||||||
|
{
|
||||||
Log("Invalid image : %s", path.c_str());
|
Log("Invalid image : %s", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -411,7 +433,8 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
|||||||
.build(*_engine);
|
.build(*_engine);
|
||||||
|
|
||||||
Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t,
|
Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t,
|
||||||
void *data) {
|
void *data)
|
||||||
|
{
|
||||||
delete reinterpret_cast<LinearImage *>(data);
|
delete reinterpret_cast<LinearImage *>(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -426,43 +449,54 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
|||||||
_resourceLoaderWrapper->free(rb);
|
_resourceLoaderWrapper->free(rb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadTextureFromPath(string path) {
|
void FilamentViewer::loadTextureFromPath(string path)
|
||||||
|
{
|
||||||
string ktxExt(".ktx");
|
string ktxExt(".ktx");
|
||||||
string ktx2Ext(".ktx2");
|
string ktx2Ext(".ktx2");
|
||||||
string pngExt(".png");
|
string pngExt(".png");
|
||||||
|
|
||||||
if (path.length() < 5) {
|
if (path.length() < 5)
|
||||||
|
{
|
||||||
Log("Invalid resource path : %s", path.c_str());
|
Log("Invalid resource path : %s", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceBuffer rb = _resourceLoaderWrapper->load(path.c_str());
|
ResourceBuffer rb = _resourceLoaderWrapper->load(path.c_str());
|
||||||
|
|
||||||
if(endsWith(path, ktxExt)) {
|
if (endsWith(path, ktxExt))
|
||||||
|
{
|
||||||
loadKtxTexture(path, rb);
|
loadKtxTexture(path, rb);
|
||||||
} else if(endsWith(path, ktx2Ext)) {
|
}
|
||||||
|
else if (endsWith(path, ktx2Ext))
|
||||||
|
{
|
||||||
loadKtx2Texture(path, rb);
|
loadKtx2Texture(path, rb);
|
||||||
} else if(endsWith(path, pngExt)) {
|
}
|
||||||
|
else if (endsWith(path, pngExt))
|
||||||
|
{
|
||||||
loadPngTexture(path, rb);
|
loadPngTexture(path, rb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a) {
|
void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a)
|
||||||
|
{
|
||||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||||
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a));
|
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a));
|
||||||
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::clearBackgroundImage() {
|
void FilamentViewer::clearBackgroundImage()
|
||||||
|
{
|
||||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||||
if (_imageTexture) {
|
if (_imageTexture)
|
||||||
|
{
|
||||||
_engine->destroy(_imageTexture);
|
_engine->destroy(_imageTexture);
|
||||||
_imageTexture = nullptr;
|
_imageTexture = nullptr;
|
||||||
Log("Destroyed background image texture");
|
Log("Destroyed background image texture");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeight) {
|
void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeight)
|
||||||
|
{
|
||||||
|
|
||||||
string resourcePathString(resourcePath);
|
string resourcePathString(resourcePath);
|
||||||
|
|
||||||
@@ -477,13 +511,15 @@ void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeigh
|
|||||||
const Viewport &vp = _view->getViewport();
|
const Viewport &vp = _view->getViewport();
|
||||||
Log("Image width %d height %d vp width %d height %d", _imageWidth, _imageHeight, vp.width, vp.height);
|
Log("Image width %d height %d vp width %d height %d", _imageWidth, _imageHeight, vp.width, vp.height);
|
||||||
|
|
||||||
|
|
||||||
float xScale = float(vp.width) / float(_imageWidth);
|
float xScale = float(vp.width) / float(_imageWidth);
|
||||||
|
|
||||||
float yScale;
|
float yScale;
|
||||||
if(fillHeight) {
|
if (fillHeight)
|
||||||
|
{
|
||||||
yScale = 1.0f;
|
yScale = 1.0f;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
yScale = float(vp.height) / float(_imageHeight);
|
yScale = float(vp.height) / float(_imageHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,17 +528,16 @@ void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeigh
|
|||||||
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
||||||
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
||||||
_imageMaterial->setDefaultParameter("showImage", 1);
|
_imageMaterial->setDefaultParameter("showImage", 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Translates the background image by (x,y) pixels.
|
/// Translates the background image by (x,y) pixels.
|
||||||
/// If clamp is true, x/y are both clamped so that the left/top and right/bottom sides of the background image
|
/// If clamp is true, x/y are both clamped so that the left/top and right/bottom sides of the background image
|
||||||
/// are positioned at a max/min of -1/1 respectively
|
/// are positioned at a max/min of -1/1 respectively
|
||||||
/// (i.e. you cannot set a position where the left/top or right/bottom sides would be "inside" the screen coordinate space).
|
/// (i.e. you cannot set a position where the left/top or right/bottom sides would be "inside" the screen coordinate space).
|
||||||
///
|
///
|
||||||
void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=false) {
|
void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp = false)
|
||||||
|
{
|
||||||
|
|
||||||
// to translate the background image, we apply a transform to the UV coordinates of the quad texture, not the quad itself (see image.mat).
|
// to translate the background image, we apply a transform to the UV coordinates of the quad texture, not the quad itself (see image.mat).
|
||||||
// this allows us to set a background colour for the quad when the texture has been translated outside the quad's bounds.
|
// this allows us to set a background colour for the quad when the texture has been translated outside the quad's bounds.
|
||||||
@@ -517,7 +552,8 @@ void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=fal
|
|||||||
// y *= _imageScale[1][1];
|
// y *= _imageScale[1][1];
|
||||||
|
|
||||||
// TODO - I haven't updated the clamp calculations to work with scaled image width/height percentages so the below code is probably wrong, don't use it until it's fixed.
|
// TODO - I haven't updated the clamp calculations to work with scaled image width/height percentages so the below code is probably wrong, don't use it until it's fixed.
|
||||||
if(clamp) {
|
if (clamp)
|
||||||
|
{
|
||||||
Log("Clamping background image translation");
|
Log("Clamping background image translation");
|
||||||
// first, clamp x/y
|
// first, clamp x/y
|
||||||
auto xScale = float(_imageWidth) / _view->getViewport().width;
|
auto xScale = float(_imageWidth) / _view->getViewport().width;
|
||||||
@@ -530,20 +566,26 @@ void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=fal
|
|||||||
|
|
||||||
// we need to clamp x so that it can only be translated between (left side touching viewport left) and (right side touching viewport right)
|
// we need to clamp x so that it can only be translated between (left side touching viewport left) and (right side touching viewport right)
|
||||||
// if width is less than viewport, these values are 0/1-xScale respectively
|
// if width is less than viewport, these values are 0/1-xScale respectively
|
||||||
if(xScale < 1) {
|
if (xScale < 1)
|
||||||
|
{
|
||||||
xMin = 0;
|
xMin = 0;
|
||||||
xMax = 1 - xScale;
|
xMax = 1 - xScale;
|
||||||
// otherwise, these value are (xScale-1 and 1-xScale)
|
// otherwise, these value are (xScale-1 and 1-xScale)
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
xMin = 1 - xScale;
|
xMin = 1 - xScale;
|
||||||
xMax = 0;
|
xMax = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do the same for y
|
// do the same for y
|
||||||
if(yScale < 1) {
|
if (yScale < 1)
|
||||||
|
{
|
||||||
yMin = 0;
|
yMin = 0;
|
||||||
yMax = 1 - yScale;
|
yMax = 1 - yScale;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
yMin = 1 - yScale;
|
yMin = 1 - yScale;
|
||||||
yMax = 0;
|
yMax = 0;
|
||||||
}
|
}
|
||||||
@@ -558,26 +600,27 @@ void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=fal
|
|||||||
y = -y;
|
y = -y;
|
||||||
Log("x %f y %f", x, y);
|
Log("x %f y %f", x, y);
|
||||||
|
|
||||||
Log("imageScale %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", _imageScale[0][0],_imageScale[0][1],_imageScale[0][2], _imageScale[0][3], \
|
Log("imageScale %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", _imageScale[0][0], _imageScale[0][1], _imageScale[0][2], _imageScale[0][3],
|
||||||
_imageScale[1][0],_imageScale[1][1],_imageScale[1][2], _imageScale[1][3],\
|
_imageScale[1][0], _imageScale[1][1], _imageScale[1][2], _imageScale[1][3],
|
||||||
_imageScale[2][0],_imageScale[2][1],_imageScale[2][2], _imageScale[2][3], \
|
_imageScale[2][0], _imageScale[2][1], _imageScale[2][2], _imageScale[2][3],
|
||||||
_imageScale[3][0], _imageScale[3][1], _imageScale[3][2], _imageScale[3][3]);
|
_imageScale[3][0], _imageScale[3][1], _imageScale[3][2], _imageScale[3][3]);
|
||||||
|
|
||||||
auto transform = math::mat4f::translation(math::float3(x, y, 0.0f)) * _imageScale;
|
auto transform = math::mat4f::translation(math::float3(x, y, 0.0f)) * _imageScale;
|
||||||
|
|
||||||
|
Log("transform %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", transform[0][0], transform[0][1], transform[0][2], transform[0][3],
|
||||||
Log("transform %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", transform[0][0],transform[0][1],transform[0][2], transform[0][3], \
|
transform[1][0], transform[1][1], transform[1][2], transform[1][3],
|
||||||
transform[1][0],transform[1][1],transform[1][2], transform[1][3],\
|
transform[2][0], transform[2][1], transform[2][2], transform[2][3],
|
||||||
transform[2][0],transform[2][1],transform[2][2], transform[2][3], \
|
|
||||||
transform[3][0], transform[3][1], transform[3][2], transform[3][3]);
|
transform[3][0], transform[3][1], transform[3][2], transform[3][3]);
|
||||||
_imageMaterial->setDefaultParameter("transform", transform);
|
_imageMaterial->setDefaultParameter("transform", transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
FilamentViewer::~FilamentViewer() {
|
FilamentViewer::~FilamentViewer()
|
||||||
|
{
|
||||||
clearAssets();
|
clearAssets();
|
||||||
delete _assetManager;
|
delete _assetManager;
|
||||||
|
|
||||||
for(auto it : _lights) {
|
for (auto it : _lights)
|
||||||
|
{
|
||||||
_engine->destroy(it);
|
_engine->destroy(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,21 +636,26 @@ FilamentViewer::~FilamentViewer() {
|
|||||||
|
|
||||||
Renderer *FilamentViewer::getRenderer() { return _renderer; }
|
Renderer *FilamentViewer::getRenderer() { return _renderer; }
|
||||||
|
|
||||||
void FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height) {
|
void FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
||||||
#else
|
#else
|
||||||
if(window) {
|
if (window)
|
||||||
|
{
|
||||||
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||||
Log("Created window swapchain.");
|
Log("Created window swapchain.");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Log("Created headless swapchain.");
|
Log("Created headless swapchain.");
|
||||||
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32_t height) {
|
void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
// Create filament textures and render targets (note the color buffer has the import call)
|
// Create filament textures and render targets (note the color buffer has the import call)
|
||||||
_rtColor = filament::Texture::Builder()
|
_rtColor = filament::Texture::Builder()
|
||||||
.width(width)
|
.width(width)
|
||||||
@@ -633,11 +681,12 @@ void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32
|
|||||||
_view->setRenderTarget(_rt);
|
_view->setRenderTarget(_rt);
|
||||||
|
|
||||||
Log("Set render target for texture id %u to %u x %u", texture, width, height);
|
Log("Set render target for texture id %u to %u x %u", texture, width, height);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::destroySwapChain() {
|
void FilamentViewer::destroySwapChain()
|
||||||
if(_rt) {
|
{
|
||||||
|
if (_rt)
|
||||||
|
{
|
||||||
_view->setRenderTarget(nullptr);
|
_view->setRenderTarget(nullptr);
|
||||||
_engine->destroy(_rtDepth);
|
_engine->destroy(_rtDepth);
|
||||||
_engine->destroy(_rtColor);
|
_engine->destroy(_rtColor);
|
||||||
@@ -646,16 +695,19 @@ void FilamentViewer::destroySwapChain() {
|
|||||||
_rtDepth = nullptr;
|
_rtDepth = nullptr;
|
||||||
_rtColor = nullptr;
|
_rtColor = nullptr;
|
||||||
}
|
}
|
||||||
if (_swapChain) {
|
if (_swapChain)
|
||||||
|
{
|
||||||
_engine->destroy(_swapChain);
|
_engine->destroy(_swapChain);
|
||||||
_swapChain = nullptr;
|
_swapChain = nullptr;
|
||||||
Log("Swapchain destroyed.");
|
Log("Swapchain destroyed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::clearAssets() {
|
void FilamentViewer::clearAssets()
|
||||||
|
{
|
||||||
Log("Clearing all assets");
|
Log("Clearing all assets");
|
||||||
if(_mainCamera) {
|
if (_mainCamera)
|
||||||
|
{
|
||||||
_view->setCamera(_mainCamera);
|
_view->setCamera(_mainCamera);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,7 +716,8 @@ void FilamentViewer::clearAssets() {
|
|||||||
Log("Cleared all assets");
|
Log("Cleared all assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeAsset(EntityId asset) {
|
void FilamentViewer::removeAsset(EntityId asset)
|
||||||
|
{
|
||||||
Log("Removing asset from scene");
|
Log("Removing asset from scene");
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
@@ -677,7 +730,8 @@ void FilamentViewer::removeAsset(EntityId asset) {
|
|||||||
///
|
///
|
||||||
/// Set the exposure for the current active camera.
|
/// Set the exposure for the current active camera.
|
||||||
///
|
///
|
||||||
void FilamentViewer::setCameraExposure(float aperture, float shutterSpeed, float sensitivity) {
|
void FilamentViewer::setCameraExposure(float aperture, float shutterSpeed, float sensitivity)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
Log("Setting aperture (%03f) shutterSpeed (%03f) and sensitivity (%03f)", aperture, shutterSpeed, sensitivity);
|
Log("Setting aperture (%03f) shutterSpeed (%03f) and sensitivity (%03f)", aperture, shutterSpeed, sensitivity);
|
||||||
cam.setExposure(aperture, shutterSpeed, sensitivity);
|
cam.setExposure(aperture, shutterSpeed, sensitivity);
|
||||||
@@ -686,7 +740,8 @@ void FilamentViewer::setCameraExposure(float aperture, float shutterSpeed, float
|
|||||||
///
|
///
|
||||||
/// Set the focal length of the active camera.
|
/// Set the focal length of the active camera.
|
||||||
///
|
///
|
||||||
void FilamentViewer::setCameraFocalLength(float focalLength) {
|
void FilamentViewer::setCameraFocalLength(float focalLength)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
_cameraFocalLength = focalLength;
|
_cameraFocalLength = focalLength;
|
||||||
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
|
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
|
||||||
@@ -696,7 +751,8 @@ void FilamentViewer::setCameraFocalLength(float focalLength) {
|
|||||||
///
|
///
|
||||||
/// Set the focus distance of the active camera.
|
/// Set the focus distance of the active camera.
|
||||||
///
|
///
|
||||||
void FilamentViewer::setCameraFocusDistance(float focusDistance) {
|
void FilamentViewer::setCameraFocusDistance(float focusDistance)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
_cameraFocusDistance = focusDistance;
|
_cameraFocusDistance = focusDistance;
|
||||||
cam.setFocusDistance(_cameraFocusDistance);
|
cam.setFocusDistance(_cameraFocusDistance);
|
||||||
@@ -707,15 +763,18 @@ void FilamentViewer::setCameraFocusDistance(float focusDistance) {
|
|||||||
/// N.B. Blender will generally export a three-node hierarchy -
|
/// N.B. Blender will generally export a three-node hierarchy -
|
||||||
/// Camera1->Camera_Orientation->Camera2. The correct name will be the Camera_Orientation.
|
/// Camera1->Camera_Orientation->Camera2. The correct name will be the Camera_Orientation.
|
||||||
///
|
///
|
||||||
bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName) {
|
bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName)
|
||||||
|
{
|
||||||
|
|
||||||
auto asset = _assetManager->getAssetByEntityId(entityId);
|
auto asset = _assetManager->getAssetByEntityId(entityId);
|
||||||
if(!asset) {
|
if (!asset)
|
||||||
|
{
|
||||||
Log("Failed to find asset under entity id %d.", entityId);
|
Log("Failed to find asset under entity id %d.", entityId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
size_t count = asset->getCameraEntityCount();
|
size_t count = asset->getCameraEntityCount();
|
||||||
if (count == 0) {
|
if (count == 0)
|
||||||
|
{
|
||||||
Log("No cameras found attached to specified entity.");
|
Log("No cameras found attached to specified entity.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -724,28 +783,35 @@ bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName) {
|
|||||||
|
|
||||||
utils::Entity target;
|
utils::Entity target;
|
||||||
|
|
||||||
if(!cameraName) {
|
if (!cameraName)
|
||||||
|
{
|
||||||
auto inst = _ncm->getInstance(cameras[0]);
|
auto inst = _ncm->getInstance(cameras[0]);
|
||||||
const char *name = _ncm->getName(inst);
|
const char *name = _ncm->getName(inst);
|
||||||
target = cameras[0];
|
target = cameras[0];
|
||||||
Log("No camera specified, using first camera node found (%s)", name);
|
Log("No camera specified, using first camera node found (%s)", name);
|
||||||
} else {
|
}
|
||||||
for (int j = 0; j < count; j++) {
|
else
|
||||||
|
{
|
||||||
|
for (int j = 0; j < count; j++)
|
||||||
|
{
|
||||||
auto inst = _ncm->getInstance(cameras[j]);
|
auto inst = _ncm->getInstance(cameras[j]);
|
||||||
const char *name = _ncm->getName(inst);
|
const char *name = _ncm->getName(inst);
|
||||||
if (strcmp(name, cameraName) == 0) {
|
if (strcmp(name, cameraName) == 0)
|
||||||
|
{
|
||||||
target = cameras[j];
|
target = cameras[j];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(target.isNull()) {
|
if (target.isNull())
|
||||||
|
{
|
||||||
Log("Unable to locate camera under name %s ", cameraName);
|
Log("Unable to locate camera under name %s ", cameraName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera *camera = _engine->getCameraComponent(target);
|
Camera *camera = _engine->getCameraComponent(target);
|
||||||
if(!camera) {
|
if (!camera)
|
||||||
|
{
|
||||||
Log("Failed to retrieve camera component for target");
|
Log("Failed to retrieve camera component for target");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -761,11 +827,13 @@ bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadSkybox(const char *const skyboxPath) {
|
void FilamentViewer::loadSkybox(const char *const skyboxPath)
|
||||||
|
{
|
||||||
|
|
||||||
removeSkybox();
|
removeSkybox();
|
||||||
|
|
||||||
if (!skyboxPath) {
|
if (!skyboxPath)
|
||||||
|
{
|
||||||
Log("No skybox path provided, removed skybox.");
|
Log("No skybox path provided, removed skybox.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,7 +844,8 @@ void FilamentViewer::loadSkybox(const char *const skyboxPath) {
|
|||||||
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
||||||
ResourceBuffer *skyboxBufferCopy = new ResourceBuffer(skyboxBuffer);
|
ResourceBuffer *skyboxBufferCopy = new ResourceBuffer(skyboxBuffer);
|
||||||
|
|
||||||
if(skyboxBuffer.size <= 0) {
|
if (skyboxBuffer.size <= 0)
|
||||||
|
{
|
||||||
Log("Could not load skybox resource.");
|
Log("Could not load skybox resource.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -790,37 +859,43 @@ void FilamentViewer::loadSkybox(const char *const skyboxPath) {
|
|||||||
static_cast<uint32_t>(skyboxBuffer.size));
|
static_cast<uint32_t>(skyboxBuffer.size));
|
||||||
|
|
||||||
_skyboxTexture =
|
_skyboxTexture =
|
||||||
ktxreader::Ktx1Reader::createTexture(_engine, *skyboxBundle, false, [](void* userdata) {
|
ktxreader::Ktx1Reader::createTexture(
|
||||||
|
_engine, *skyboxBundle, false, [](void *userdata)
|
||||||
|
{
|
||||||
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
||||||
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
||||||
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
||||||
loader->free(*rb);
|
loader->free(*rb);
|
||||||
delete rb;
|
delete rb;
|
||||||
delete vec;
|
delete vec;
|
||||||
Log("Skybox load complete.");
|
Log("Skybox load complete."); },
|
||||||
}, callbackData);
|
callbackData);
|
||||||
_skybox =
|
_skybox =
|
||||||
filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine);
|
filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine);
|
||||||
|
|
||||||
_scene->setSkybox(_skybox);
|
_scene->setSkybox(_skybox);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeSkybox() {
|
void FilamentViewer::removeSkybox()
|
||||||
|
{
|
||||||
Log("Removing skybox");
|
Log("Removing skybox");
|
||||||
_scene->setSkybox(nullptr);
|
_scene->setSkybox(nullptr);
|
||||||
if(_skybox) {
|
if (_skybox)
|
||||||
|
{
|
||||||
_engine->destroy(_skybox);
|
_engine->destroy(_skybox);
|
||||||
_skybox = nullptr;
|
_skybox = nullptr;
|
||||||
}
|
}
|
||||||
if(_skyboxTexture) {
|
if (_skyboxTexture)
|
||||||
|
{
|
||||||
_engine->destroy(_skyboxTexture);
|
_engine->destroy(_skyboxTexture);
|
||||||
_skyboxTexture = nullptr;
|
_skyboxTexture = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeIbl() {
|
void FilamentViewer::removeIbl()
|
||||||
if(_indirectLight) {
|
{
|
||||||
|
if (_indirectLight)
|
||||||
|
{
|
||||||
_engine->destroy(_indirectLight);
|
_engine->destroy(_indirectLight);
|
||||||
_engine->destroy(_iblTexture);
|
_engine->destroy(_iblTexture);
|
||||||
_indirectLight = nullptr;
|
_indirectLight = nullptr;
|
||||||
@@ -829,9 +904,11 @@ void FilamentViewer::removeIbl() {
|
|||||||
_scene->setIndirectLight(nullptr);
|
_scene->setIndirectLight(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadIbl(const char *const iblPath, float intensity) {
|
void FilamentViewer::loadIbl(const char *const iblPath, float intensity)
|
||||||
|
{
|
||||||
removeIbl();
|
removeIbl();
|
||||||
if (iblPath) {
|
if (iblPath)
|
||||||
|
{
|
||||||
Log("Loading IBL from %s", iblPath);
|
Log("Loading IBL from %s", iblPath);
|
||||||
|
|
||||||
// Load IBL.
|
// Load IBL.
|
||||||
@@ -839,7 +916,8 @@ void FilamentViewer::loadIbl(const char *const iblPath, float intensity) {
|
|||||||
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
||||||
ResourceBuffer *iblBufferCopy = new ResourceBuffer(iblBuffer);
|
ResourceBuffer *iblBufferCopy = new ResourceBuffer(iblBuffer);
|
||||||
|
|
||||||
if(iblBuffer.size == 0) {
|
if (iblBuffer.size == 0)
|
||||||
|
{
|
||||||
Log("Error loading IBL, resource could not be loaded.");
|
Log("Error loading IBL, resource could not be loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -853,14 +931,16 @@ void FilamentViewer::loadIbl(const char *const iblPath, float intensity) {
|
|||||||
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, iblBufferCopy};
|
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, iblBufferCopy};
|
||||||
|
|
||||||
_iblTexture =
|
_iblTexture =
|
||||||
ktxreader::Ktx1Reader::createTexture(_engine, *iblBundle, false, [](void* userdata) {
|
ktxreader::Ktx1Reader::createTexture(
|
||||||
|
_engine, *iblBundle, false, [](void *userdata)
|
||||||
|
{
|
||||||
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
||||||
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
||||||
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
||||||
loader->free(*rb);
|
loader->free(*rb);
|
||||||
delete rb;
|
delete rb;
|
||||||
delete vec;
|
delete vec; },
|
||||||
}, callbackData);
|
callbackData);
|
||||||
_indirectLight = IndirectLight::Builder()
|
_indirectLight = IndirectLight::Builder()
|
||||||
.reflections(_iblTexture)
|
.reflections(_iblTexture)
|
||||||
.irradiance(3, harmonics)
|
.irradiance(3, harmonics)
|
||||||
@@ -879,14 +959,17 @@ void FilamentViewer::render(
|
|||||||
uint64_t frameTimeInNanos,
|
uint64_t frameTimeInNanos,
|
||||||
void *pixelBuffer,
|
void *pixelBuffer,
|
||||||
void (*callback)(void *buf, size_t size, void *data),
|
void (*callback)(void *buf, size_t size, void *data),
|
||||||
void* data) {
|
void *data)
|
||||||
|
{
|
||||||
|
|
||||||
if (!_view || !_mainCamera || !_swapChain) {
|
if (!_view || !_mainCamera || !_swapChain)
|
||||||
|
{
|
||||||
Log("Not ready for rendering");
|
Log("Not ready for rendering");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_frameCount == 60) {
|
if (_frameCount == 60)
|
||||||
|
{
|
||||||
// Log("1 sec average for asset animation update %f", _elapsed / 60);
|
// Log("1 sec average for asset animation update %f", _elapsed / 60);
|
||||||
_elapsed = 0;
|
_elapsed = 0;
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
@@ -899,7 +982,18 @@ void FilamentViewer::render(
|
|||||||
_elapsed += tmr.elapsed();
|
_elapsed += tmr.elapsed();
|
||||||
_frameCount++;
|
_frameCount++;
|
||||||
|
|
||||||
if(pixelBuffer) {
|
// if a manipulator is active, update the active camera orientation
|
||||||
|
if(_manipulator) {
|
||||||
|
math::double3 eye, target, upward;
|
||||||
|
Camera& cam =_view->getCamera();
|
||||||
|
_manipulator->getLookAt(&eye, &target, &upward);
|
||||||
|
cam.lookAt(eye, target, upward);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - this was an experiment but probably useful to keep for debugging
|
||||||
|
// if pixelBuffer is provided, we will copy the framebuffer into the pixelBuffer.
|
||||||
|
if (pixelBuffer)
|
||||||
|
{
|
||||||
auto pbd = Texture::PixelBufferDescriptor(
|
auto pbd = Texture::PixelBufferDescriptor(
|
||||||
pixelBuffer, size_t(1024 * 768 * 4),
|
pixelBuffer, size_t(1024 * 768 * 4),
|
||||||
Texture::Format::RGBA,
|
Texture::Format::RGBA,
|
||||||
@@ -909,21 +1003,27 @@ void FilamentViewer::render(
|
|||||||
_renderer->render(_view);
|
_renderer->render(_view);
|
||||||
_renderer->readPixels(0, 0, 1024, 768, std::move(pbd));
|
_renderer->readPixels(0, 0, 1024, 768, std::move(pbd));
|
||||||
_renderer->endFrame();
|
_renderer->endFrame();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Render the scene, unless the renderer wants to skip the frame.
|
// Render the scene, unless the renderer wants to skip the frame.
|
||||||
if (_renderer->beginFrame(_swapChain, frameTimeInNanos)) {
|
if (_renderer->beginFrame(_swapChain, frameTimeInNanos))
|
||||||
|
{
|
||||||
_renderer->render(_view);
|
_renderer->render(_view);
|
||||||
_renderer->endFrame();
|
_renderer->endFrame();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// skipped frame
|
// skipped frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::updateViewportAndCameraProjection(
|
void FilamentViewer::updateViewportAndCameraProjection(
|
||||||
int width, int height, float contentScaleFactor) {
|
int width, int height, float contentScaleFactor)
|
||||||
if (!_view || !_mainCamera) {
|
{
|
||||||
|
if (!_view || !_mainCamera)
|
||||||
|
{
|
||||||
Log("Skipping camera update, no view or camrea");
|
Log("Skipping camera update, no view or camrea");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -944,20 +1044,24 @@ void FilamentViewer::updateViewportAndCameraProjection(
|
|||||||
contentScaleFactor);
|
contentScaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setViewFrustumCulling(bool enabled) {
|
void FilamentViewer::setViewFrustumCulling(bool enabled)
|
||||||
|
{
|
||||||
_view->setFrustumCullingEnabled(enabled);
|
_view->setFrustumCullingEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setCameraPosition(float x, float y, float z) {
|
void FilamentViewer::setCameraPosition(float x, float y, float z)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
|
|
||||||
_cameraPosition = math::mat4f::translation(math::float3(x, y, z));
|
_cameraPosition = math::mat4f::translation(math::float3(x, y, z));
|
||||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
void FilamentViewer::moveCameraToAsset(EntityId entityId)
|
||||||
|
{
|
||||||
auto asset = _assetManager->getAssetByEntityId(entityId);
|
auto asset = _assetManager->getAssetByEntityId(entityId);
|
||||||
if(!asset) {
|
if (!asset)
|
||||||
|
{
|
||||||
Log("Failed to find asset attached to specified entity id.");
|
Log("Failed to find asset attached to specified entity id.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -971,13 +1075,15 @@ void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
|||||||
Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar());
|
Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setCameraRotation(float rads, float x, float y, float z) {
|
void FilamentViewer::setCameraRotation(float rads, float x, float y, float z)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
_cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z));
|
_cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z));
|
||||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setCameraModelMatrix(const float* const matrix) {
|
void FilamentViewer::setCameraModelMatrix(const float *const matrix)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
|
|
||||||
mat4 modelMatrix(
|
mat4 modelMatrix(
|
||||||
@@ -996,75 +1102,123 @@ void FilamentViewer::setCameraModelMatrix(const float* const matrix) {
|
|||||||
matrix[12],
|
matrix[12],
|
||||||
matrix[13],
|
matrix[13],
|
||||||
matrix[14],
|
matrix[14],
|
||||||
matrix[15]
|
matrix[15]);
|
||||||
);
|
|
||||||
cam.setModelMatrix(modelMatrix);
|
cam.setModelMatrix(modelMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::grabBegin(float x, float y, bool pan) {
|
void FilamentViewer::_createManipulator()
|
||||||
if (!_view || !_mainCamera || !_swapChain) {
|
{
|
||||||
Log("View not ready, ignoring grab");
|
Camera &cam = _view->getCamera();
|
||||||
return;
|
math::double3 home = cam.getPosition();
|
||||||
}
|
math::double3 up = cam.getUpVector();
|
||||||
_panning = pan;
|
math::double3 target = home + cam.getForwardVector();
|
||||||
_startX = x;
|
Viewport const &vp = _view->getViewport();
|
||||||
_startY = y;
|
Log("Creating manipulator for viewport size %dx%d with camera norm %f", vp.width, vp.height, norm(home));
|
||||||
|
float zoomSpeed = norm(home) / 10;
|
||||||
|
zoomSpeed = math::clamp(zoomSpeed, 0.1f, 100000.0f);
|
||||||
|
|
||||||
|
_manipulator = Manipulator<double>::Builder()
|
||||||
|
.viewport(vp.width, vp.height)
|
||||||
|
.orbitHomePosition(home[0], home[1], home[2])
|
||||||
|
.upVector(up.x, up.y, up.z)
|
||||||
|
.zoomSpeed(zoomSpeed)
|
||||||
|
// .orbitSpeed(0.0001, 0.0001)
|
||||||
|
.targetPosition(target[0], target[1], target[2])
|
||||||
|
.build(Mode::ORBIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::grabUpdate(float x, float y) {
|
void FilamentViewer::grabBegin(float x, float y, bool pan)
|
||||||
if (!_view || !_swapChain) {
|
{
|
||||||
|
if (!_view || !_mainCamera || !_swapChain)
|
||||||
|
{
|
||||||
Log("View not ready, ignoring grab");
|
Log("View not ready, ignoring grab");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Camera& cam =_view->getCamera();
|
if (!_manipulator)
|
||||||
auto eye = cam.getPosition();
|
{
|
||||||
auto target = eye + cam.getForwardVector();
|
_createManipulator();
|
||||||
auto upward = cam.getUpVector();
|
}
|
||||||
Viewport const& vp = _view->getViewport();
|
if(pan) {
|
||||||
if(_panning) {
|
Log("Beginning pan at %f %f", x, y);
|
||||||
auto trans = cam.getModelMatrix() * mat4::translation(math::float3 { 50 * (x - _startX) / vp.width, 50 * (y - _startY) / vp.height, 0.0f });
|
|
||||||
cam.setModelMatrix(trans);
|
|
||||||
} else {
|
} else {
|
||||||
auto trans = cam.getModelMatrix() * mat4::rotation(0.05,
|
Log("Beginning rotate at %f %f", x, y);
|
||||||
math::float3 { (y - _startY) / vp.height, (x - _startX) / vp.width, 0.0f });
|
|
||||||
cam.setModelMatrix(trans);
|
|
||||||
}
|
|
||||||
_startX = x;
|
|
||||||
_startY = y;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::grabEnd() {
|
_manipulator->grabBegin(x, y, pan);
|
||||||
if (!_view || !_mainCamera || !_swapChain) {
|
}
|
||||||
|
|
||||||
|
void FilamentViewer::grabUpdate(float x, float y)
|
||||||
|
{
|
||||||
|
if (!_view || !_swapChain)
|
||||||
|
{
|
||||||
Log("View not ready, ignoring grab");
|
Log("View not ready, ignoring grab");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log("Updating grab at %f %f", x, y);
|
||||||
|
|
||||||
|
|
||||||
|
if (_manipulator)
|
||||||
|
{
|
||||||
|
_manipulator->grabUpdate(x, y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Error - trying to use a manipulator when one is not available. Ensure you call grabBegin before grabUpdate/grabEnd");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::scrollBegin() {
|
void FilamentViewer::grabEnd()
|
||||||
// noop
|
{
|
||||||
|
if (!_view || !_mainCamera || !_swapChain)
|
||||||
|
{
|
||||||
|
Log("View not ready, ignoring grab");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_manipulator)
|
||||||
|
{
|
||||||
|
_manipulator->grabEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Error - trying to use a manipulator when one is not available. Ensure you call grabBegin before grabUpdate/grabEnd");
|
||||||
|
}
|
||||||
|
delete _manipulator;
|
||||||
|
_manipulator = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::scrollUpdate(float x, float y, float delta) {
|
void FilamentViewer::scrollBegin()
|
||||||
Camera& cam =_view->getCamera();
|
{
|
||||||
Viewport const& vp = _view->getViewport();
|
if (!_manipulator)
|
||||||
auto trans = cam.getModelMatrix() * mat4::translation(math::float3 {0.0f, 0.0f, delta });
|
{
|
||||||
cam.setModelMatrix(trans);
|
_createManipulator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::scrollEnd() {
|
void FilamentViewer::scrollUpdate(float x, float y, float delta)
|
||||||
// noop
|
{
|
||||||
|
if (_manipulator)
|
||||||
|
{
|
||||||
|
_manipulator->scroll(int(x), int(y), delta);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Error - trying to use a manipulator when one is not available. Ensure you call grabBegin before grabUpdate/grabEnd");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId* entityId) {
|
void FilamentViewer::scrollEnd()
|
||||||
Log("Picking at %d,%d", x, y);
|
{
|
||||||
|
delete _manipulator;
|
||||||
|
_manipulator = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId)
|
||||||
|
{
|
||||||
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result) {
|
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result) {
|
||||||
|
|
||||||
*entityId = Entity::smuggle(result.renderable);
|
*entityId = Entity::smuggle(result.renderable);
|
||||||
Log("Got result %d", *entityId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace polyvox
|
} // namespace polyvox
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
98
ios/src/camutils/Bookmark.cpp
Normal file
98
ios/src/camutils/Bookmark.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <camutils/Bookmark.h>
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
#include <math/vec3.h>
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
Bookmark<FLOAT> Bookmark<FLOAT>::interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t) {
|
||||||
|
Bookmark<FLOAT> result;
|
||||||
|
using float3 = filament::math::vec3<FLOAT>;
|
||||||
|
|
||||||
|
if (a.mode == Mode::MAP) {
|
||||||
|
assert(b.mode == Mode::MAP);
|
||||||
|
const double rho = sqrt(2.0);
|
||||||
|
const double rho2 = 2, rho4 = 4;
|
||||||
|
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
|
||||||
|
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
|
||||||
|
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
|
||||||
|
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
|
||||||
|
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
|
||||||
|
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
|
||||||
|
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
|
||||||
|
const double dr = r1 - r0;
|
||||||
|
const int valid = !std::isnan(dr) && dr != 0;
|
||||||
|
const double S = (valid ? dr : log(w1 / w0)) / rho;
|
||||||
|
const double s = t * S;
|
||||||
|
|
||||||
|
// This performs Van Wijk interpolation to animate between two waypoints on a map.
|
||||||
|
if (valid) {
|
||||||
|
const double coshr0 = cosh(r0);
|
||||||
|
const double u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
|
||||||
|
Bookmark<FLOAT> result;
|
||||||
|
result.map.center.x = ux0 + u * dx;
|
||||||
|
result.map.center.y = uy0 + u * dy;
|
||||||
|
result.map.extent = w0 * coshr0 / cosh(rho * s + r0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For degenerate cases, fall back to a simplified interpolation method.
|
||||||
|
result.map.center.x = ux0 + t * dx;
|
||||||
|
result.map.center.y = uy0 + t * dy;
|
||||||
|
result.map.extent = w0 * exp(rho * s);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(b.mode == Mode::ORBIT);
|
||||||
|
result.orbit.phi = lerp(a.orbit.phi, b.orbit.phi, FLOAT(t));
|
||||||
|
result.orbit.theta = lerp(a.orbit.theta, b.orbit.theta, FLOAT(t));
|
||||||
|
result.orbit.distance = lerp(a.orbit.distance, b.orbit.distance, FLOAT(t));
|
||||||
|
result.orbit.pivot = lerp(a.orbit.pivot, b.orbit.pivot, float3(t));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses the Van Wijk method to suggest a duration for animating between two waypoints on a map.
|
||||||
|
// This does not have units, so just use it as a multiplier.
|
||||||
|
template <typename FLOAT>
|
||||||
|
double Bookmark<FLOAT>::duration(Bookmark<FLOAT> a, Bookmark<FLOAT> b) {
|
||||||
|
assert(a.mode == Mode::ORBIT && b.mode == Mode::ORBIT);
|
||||||
|
const double rho = sqrt(2.0);
|
||||||
|
const double rho2 = 2, rho4 = 4;
|
||||||
|
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
|
||||||
|
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
|
||||||
|
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
|
||||||
|
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
|
||||||
|
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
|
||||||
|
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
|
||||||
|
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
|
||||||
|
const double dr = r1 - r0;
|
||||||
|
const int valid = !std::isnan(dr) && dr != 0;
|
||||||
|
const double S = (valid ? dr : log(w1 / w0)) / rho;
|
||||||
|
return fabs(S);
|
||||||
|
}
|
||||||
|
|
||||||
|
template class Bookmark<float>;
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
206
ios/src/camutils/FreeFlightManipulator.h
Normal file
206
ios/src/camutils/FreeFlightManipulator.h
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAMUTILS_FREEFLIGHT_MANIPULATOR_H
|
||||||
|
#define CAMUTILS_FREEFLIGHT_MANIPULATOR_H
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
#include <math/mat3.h>
|
||||||
|
#include <math/mat4.h>
|
||||||
|
#include <math/quat.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
class FreeFlightManipulator : public Manipulator<FLOAT> {
|
||||||
|
public:
|
||||||
|
using vec2 = filament::math::vec2<FLOAT>;
|
||||||
|
using vec3 = filament::math::vec3<FLOAT>;
|
||||||
|
using vec4 = filament::math::vec4<FLOAT>;
|
||||||
|
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||||
|
using Base = Manipulator<FLOAT>;
|
||||||
|
using Config = typename Base::Config;
|
||||||
|
|
||||||
|
FreeFlightManipulator(Mode mode, const Config& props) : Base(mode, props) {
|
||||||
|
setProperties(props);
|
||||||
|
Base::mEye = Base::mProps.flightStartPosition;
|
||||||
|
const auto pitch = Base::mProps.flightStartPitch;
|
||||||
|
const auto yaw = Base::mProps.flightStartYaw;
|
||||||
|
mTargetEuler = {pitch, yaw};
|
||||||
|
updateTarget(pitch, yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProperties(const Config& props) override {
|
||||||
|
Config resolved = props;
|
||||||
|
|
||||||
|
if (resolved.flightPanSpeed == vec2(0, 0)) {
|
||||||
|
resolved.flightPanSpeed = vec2(0.01, 0.01);
|
||||||
|
}
|
||||||
|
if (resolved.flightMaxSpeed == 0.0) {
|
||||||
|
resolved.flightMaxSpeed = 10.0;
|
||||||
|
}
|
||||||
|
if (resolved.flightSpeedSteps == 0) {
|
||||||
|
resolved.flightSpeedSteps = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
Base::setProperties(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTarget(FLOAT pitch, FLOAT yaw) {
|
||||||
|
Base::mTarget = Base::mEye + (mat3::eulerZYX(0, yaw, pitch) * vec3(0.0, 0.0, -1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabBegin(int x, int y, bool strafe) override {
|
||||||
|
mGrabWin = {x, y};
|
||||||
|
mGrabbing = true;
|
||||||
|
mGrabEuler = mTargetEuler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabUpdate(int x, int y) override {
|
||||||
|
if (!mGrabbing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vec2 del = mGrabWin - vec2{x, y};
|
||||||
|
|
||||||
|
const auto& grabPitch = mGrabEuler.x;
|
||||||
|
const auto& grabYaw = mGrabEuler.y;
|
||||||
|
auto& pitch = mTargetEuler.x;
|
||||||
|
auto& yaw = mTargetEuler.y;
|
||||||
|
|
||||||
|
constexpr double EPSILON = 0.001;
|
||||||
|
|
||||||
|
auto panSpeed = Base::mProps.flightPanSpeed;
|
||||||
|
constexpr FLOAT minPitch = (-F_PI_2 + EPSILON);
|
||||||
|
constexpr FLOAT maxPitch = ( F_PI_2 - EPSILON);
|
||||||
|
pitch = clamp(grabPitch + del.y * -panSpeed.y, minPitch, maxPitch);
|
||||||
|
yaw = fmod(grabYaw + del.x * panSpeed.x, 2.0 * F_PI);
|
||||||
|
|
||||||
|
updateTarget(pitch, yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabEnd() override {
|
||||||
|
mGrabbing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyDown(typename Base::Key key) override {
|
||||||
|
mKeyDown[(int) key] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyUp(typename Base::Key key) override {
|
||||||
|
mKeyDown[(int) key] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(int x, int y, FLOAT scrolldelta) override {
|
||||||
|
const FLOAT halfSpeedSteps = Base::mProps.flightSpeedSteps / 2;
|
||||||
|
mScrollWheel = clamp(mScrollWheel + scrolldelta, -halfSpeedSteps, halfSpeedSteps);
|
||||||
|
// Normalize the scroll position from -1 to 1 and calculate the move speed, in world
|
||||||
|
// units per second.
|
||||||
|
mScrollPositionNormalized = (mScrollWheel + halfSpeedSteps) / halfSpeedSteps - 1.0;
|
||||||
|
mMoveSpeed = pow(Base::mProps.flightMaxSpeed, mScrollPositionNormalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(FLOAT deltaTime) override {
|
||||||
|
vec3 forceLocal { 0.0, 0.0, 0.0 };
|
||||||
|
|
||||||
|
if (mKeyDown[(int) Base::Key::FORWARD]) {
|
||||||
|
forceLocal += vec3{ 0.0, 0.0, -1.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::LEFT]) {
|
||||||
|
forceLocal += vec3{ -1.0, 0.0, 0.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::BACKWARD]) {
|
||||||
|
forceLocal += vec3{ 0.0, 0.0, 1.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::RIGHT]) {
|
||||||
|
forceLocal += vec3{ 1.0, 0.0, 0.0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
|
||||||
|
vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz;
|
||||||
|
|
||||||
|
if (mKeyDown[(int) Base::Key::UP]) {
|
||||||
|
forceWorld += vec3{ 0.0, 1.0, 0.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::DOWN]) {
|
||||||
|
forceWorld += vec3{ 0.0, -1.0, 0.0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
forceWorld *= mMoveSpeed;
|
||||||
|
|
||||||
|
const auto dampingFactor = Base::mProps.flightMoveDamping;
|
||||||
|
if (dampingFactor == 0.0) {
|
||||||
|
// Without damping, we simply treat the force as our velocity.
|
||||||
|
mEyeVelocity = forceWorld;
|
||||||
|
} else {
|
||||||
|
// The dampingFactor acts as "friction", which acts upon the camera in the direction
|
||||||
|
// opposite its velocity.
|
||||||
|
// Force is also multiplied by the dampingFactor, to "make up" for the friction.
|
||||||
|
// This ensures that the max velocity still approaches mMoveSpeed;
|
||||||
|
vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor;
|
||||||
|
mEyeVelocity += velocityDelta * deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vec3 positionDelta = mEyeVelocity * deltaTime;
|
||||||
|
|
||||||
|
Base::mEye += positionDelta;
|
||||||
|
Base::mTarget += positionDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getCurrentBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.flight.position = Base::mEye;
|
||||||
|
bookmark.flight.pitch = mTargetEuler.x;
|
||||||
|
bookmark.flight.yaw = mTargetEuler.y;
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getHomeBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.flight.position = Base::mProps.flightStartPosition;
|
||||||
|
bookmark.flight.pitch = Base::mProps.flightStartPitch;
|
||||||
|
bookmark.flight.yaw = Base::mProps.flightStartYaw;
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpToBookmark(const Bookmark& bookmark) override {
|
||||||
|
Base::mEye = bookmark.flight.position;
|
||||||
|
updateTarget(bookmark.flight.pitch, bookmark.flight.yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
vec2 mGrabWin;
|
||||||
|
vec2 mTargetEuler; // (pitch, yaw)
|
||||||
|
vec2 mGrabEuler; // (pitch, yaw)
|
||||||
|
bool mKeyDown[(int) Base::Key::COUNT] = {false};
|
||||||
|
bool mGrabbing = false;
|
||||||
|
FLOAT mScrollWheel = 0.0f;
|
||||||
|
FLOAT mScrollPositionNormalized = 0.0f;
|
||||||
|
FLOAT mMoveSpeed = 1.0f;
|
||||||
|
vec3 mEyeVelocity;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
|
|
||||||
|
#endif /* CAMUTILS_FREEFLIGHT_MANIPULATOR_H */
|
||||||
324
ios/src/camutils/Manipulator.cpp
Normal file
324
ios/src/camutils/Manipulator.cpp
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
|
||||||
|
#include "FreeFlightManipulator.h"
|
||||||
|
#include "MapManipulator.h"
|
||||||
|
#include "OrbitManipulator.h"
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::viewport(int width, int height) {
|
||||||
|
details.viewport[0] = width;
|
||||||
|
details.viewport[1] = height;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.targetPosition = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::upVector(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.upVector = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::zoomSpeed(FLOAT val) {
|
||||||
|
details.zoomSpeed = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.orbitHomePosition = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitSpeed(FLOAT x, FLOAT y) {
|
||||||
|
details.orbitSpeed = {x, y};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDirection(Fov fov) {
|
||||||
|
details.fovDirection = fov;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDegrees(FLOAT degrees) {
|
||||||
|
details.fovDegrees = degrees;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::farPlane(FLOAT distance) {
|
||||||
|
details.farPlane = distance;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight) {
|
||||||
|
details.mapExtent = {worldWidth, worldHeight};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapMinDistance(FLOAT mindist) {
|
||||||
|
details.mapMinDistance = mindist;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartPosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.flightStartPosition = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw) {
|
||||||
|
details.flightStartPitch = pitch;
|
||||||
|
details.flightStartYaw = yaw;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMaxMoveSpeed(FLOAT maxSpeed) {
|
||||||
|
details.flightMaxSpeed = maxSpeed;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightSpeedSteps(int steps) {
|
||||||
|
details.flightSpeedSteps = steps;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightPanSpeed(FLOAT x, FLOAT y) {
|
||||||
|
details.flightPanSpeed = {x, y};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMoveDamping(FLOAT damping) {
|
||||||
|
details.flightMoveDamping = damping;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d) {
|
||||||
|
details.groundPlane = {a, b, c, d};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::raycastCallback(RayCallback cb, void* userdata) {
|
||||||
|
details.raycastCallback = cb;
|
||||||
|
details.raycastUserdata = userdata;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
Manipulator<FLOAT>* Manipulator<FLOAT>::Builder::build(Mode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case Mode::FREE_FLIGHT:
|
||||||
|
return new FreeFlightManipulator<FLOAT>(mode, details);
|
||||||
|
case Mode::MAP:
|
||||||
|
return new MapManipulator<FLOAT>(mode, details);
|
||||||
|
case Mode::ORBIT:
|
||||||
|
return new OrbitManipulator<FLOAT>(mode, details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
Manipulator<FLOAT>::Manipulator(Mode mode, const Config& props) : mMode(mode) {
|
||||||
|
setProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::setProperties(const Config& props) {
|
||||||
|
mProps = props;
|
||||||
|
|
||||||
|
if (mProps.zoomSpeed == FLOAT(0)) {
|
||||||
|
mProps.zoomSpeed = 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.upVector == vec3(0)) {
|
||||||
|
mProps.upVector = vec3(0, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.fovDegrees == FLOAT(0)) {
|
||||||
|
mProps.fovDegrees = 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.farPlane == FLOAT(0)) {
|
||||||
|
mProps.farPlane = 5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.mapExtent == vec2(0)) {
|
||||||
|
mProps.mapExtent = vec2(512);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::setViewport(int width, int height) {
|
||||||
|
Config props = mProps;
|
||||||
|
props.viewport[0] = width;
|
||||||
|
props.viewport[1] = height;
|
||||||
|
setProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const {
|
||||||
|
*targetPosition = mTarget;
|
||||||
|
*eyePosition = mEye;
|
||||||
|
const vec3 gaze = normalize(mTarget - mEye);
|
||||||
|
const vec3 right = cross(gaze, mProps.upVector);
|
||||||
|
*upward = cross(right, gaze);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
static bool raycastPlane(const filament::math::vec3<FLOAT>& origin,
|
||||||
|
const filament::math::vec3<FLOAT>& dir, FLOAT* t, void* userdata) {
|
||||||
|
using vec3 = filament::math::vec3<FLOAT>;
|
||||||
|
using vec4 = filament::math::vec4<FLOAT>;
|
||||||
|
auto props = (const typename Manipulator<FLOAT>::Config*) userdata;
|
||||||
|
const vec4 plane = props->groundPlane;
|
||||||
|
const vec3 n = vec3(plane[0], plane[1], plane[2]);
|
||||||
|
const vec3 p0 = n * plane[3];
|
||||||
|
const FLOAT denom = -dot(n, dir);
|
||||||
|
if (denom > 1e-6) {
|
||||||
|
const vec3 p0l0 = p0 - origin;
|
||||||
|
*t = dot(p0l0, n) / -denom;
|
||||||
|
return *t >= 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::getRay(int x, int y, vec3* porigin, vec3* pdir) const {
|
||||||
|
const vec3 gaze = normalize(mTarget - mEye);
|
||||||
|
const vec3 right = normalize(cross(gaze, mProps.upVector));
|
||||||
|
const vec3 upward = cross(right, gaze);
|
||||||
|
const FLOAT width = mProps.viewport[0];
|
||||||
|
const FLOAT height = mProps.viewport[1];
|
||||||
|
const FLOAT fov = mProps.fovDegrees * F_PI / 180.0;
|
||||||
|
|
||||||
|
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
|
||||||
|
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
|
||||||
|
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
|
||||||
|
|
||||||
|
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
|
||||||
|
const FLOAT tangent = tan(fov / 2.0);
|
||||||
|
const FLOAT aspect = width / height;
|
||||||
|
|
||||||
|
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
|
||||||
|
vec3 dir = gaze;
|
||||||
|
if (mProps.fovDirection == Fov::VERTICAL) {
|
||||||
|
dir += right * tangent * u * aspect;
|
||||||
|
dir += upward * tangent * v;
|
||||||
|
} else {
|
||||||
|
dir += right * tangent * u;
|
||||||
|
dir += upward * tangent * v / aspect;
|
||||||
|
}
|
||||||
|
dir = normalize(dir);
|
||||||
|
|
||||||
|
*porigin = mEye;
|
||||||
|
*pdir = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
bool Manipulator<FLOAT>::raycast(int x, int y, vec3* result) const {
|
||||||
|
vec3 origin, dir;
|
||||||
|
getRay(x, y, &origin, &dir);
|
||||||
|
|
||||||
|
// Choose either the user's callback function or the plane intersector.
|
||||||
|
auto callback = mProps.raycastCallback;
|
||||||
|
auto fallback = raycastPlane<FLOAT>;
|
||||||
|
void* userdata = mProps.raycastUserdata;
|
||||||
|
if (!callback) {
|
||||||
|
callback = fallback;
|
||||||
|
userdata = (void*) &mProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the ray misses, then try the fallback function.
|
||||||
|
FLOAT t;
|
||||||
|
if (!callback(mEye, dir, &t, userdata)) {
|
||||||
|
if (callback == fallback || !fallback(mEye, dir, &t, (void*) &mProps)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = mEye + dir * t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
filament::math::vec3<FLOAT> Manipulator<FLOAT>::raycastFarPlane(int x, int y) const {
|
||||||
|
const filament::math::vec3<FLOAT> gaze = normalize(mTarget - mEye);
|
||||||
|
const vec3 right = cross(gaze, mProps.upVector);
|
||||||
|
const vec3 upward = cross(right, gaze);
|
||||||
|
const FLOAT width = mProps.viewport[0];
|
||||||
|
const FLOAT height = mProps.viewport[1];
|
||||||
|
const FLOAT fov = mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
|
||||||
|
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
|
||||||
|
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
|
||||||
|
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
|
||||||
|
|
||||||
|
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
|
||||||
|
const FLOAT tangent = tan(fov / 2.0);
|
||||||
|
const FLOAT aspect = width / height;
|
||||||
|
|
||||||
|
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
|
||||||
|
vec3 dir = gaze;
|
||||||
|
if (mProps.fovDirection == Fov::VERTICAL) {
|
||||||
|
dir += right * tangent * u * aspect;
|
||||||
|
dir += upward * tangent * v;
|
||||||
|
} else {
|
||||||
|
dir += right * tangent * u;
|
||||||
|
dir += upward * tangent * v / aspect;
|
||||||
|
}
|
||||||
|
return mEye + dir * mProps.farPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::keyDown(Manipulator<FLOAT>::Key key) { }
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::keyUp(Manipulator<FLOAT>::Key key) { }
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::update(FLOAT deltaTime) { }
|
||||||
|
|
||||||
|
template class Manipulator<float>;
|
||||||
|
template class Manipulator<double>;
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
197
ios/src/camutils/MapManipulator.h
Normal file
197
ios/src/camutils/MapManipulator.h
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAMUTILS_MAP_MANIPULATOR_H
|
||||||
|
#define CAMUTILS_MAP_MANIPULATOR_H
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/vec3.h>
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
class MapManipulator : public Manipulator<FLOAT> {
|
||||||
|
public:
|
||||||
|
using vec2 = math::vec2<FLOAT>;
|
||||||
|
using vec3 = math::vec3<FLOAT>;
|
||||||
|
using vec4 = math::vec4<FLOAT>;
|
||||||
|
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||||
|
using Base = Manipulator<FLOAT>;
|
||||||
|
using Config = typename Manipulator<FLOAT>::Config;
|
||||||
|
|
||||||
|
MapManipulator(Mode mode, const Config& props) : Manipulator<FLOAT>(mode, props) {
|
||||||
|
const FLOAT width = Base::mProps.mapExtent.x;
|
||||||
|
const FLOAT height = Base::mProps.mapExtent.y;
|
||||||
|
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const FLOAT halfExtent = (horiz ? width : height) / 2.0;
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT distance = halfExtent / tan(fov / 2.0);
|
||||||
|
Base::mTarget = Base::mProps.targetPosition;
|
||||||
|
Base::mEye = Base::mTarget + distance * targetToEye;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabBegin(int x, int y, bool strafe) override {
|
||||||
|
if (strafe || !Base::raycast(x, y, &mGrabScene)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mGrabFar = Base::raycastFarPlane(x, y);
|
||||||
|
mGrabEye = Base::mEye;
|
||||||
|
mGrabTarget = Base::mTarget;
|
||||||
|
mGrabbing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabUpdate(int x, int y) override {
|
||||||
|
if (mGrabbing) {
|
||||||
|
const FLOAT ulen = distance(mGrabScene, mGrabEye);
|
||||||
|
const FLOAT vlen = distance(mGrabFar, mGrabScene);
|
||||||
|
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
|
||||||
|
const vec3 eyePosition = mGrabEye + translation;
|
||||||
|
const vec3 targetPosition = mGrabTarget + translation;
|
||||||
|
moveWithConstraints(eyePosition, targetPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabEnd() override {
|
||||||
|
mGrabbing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(int x, int y, FLOAT scrolldelta) override {
|
||||||
|
vec3 grabScene;
|
||||||
|
if (!Base::raycast(x, y, &grabScene)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the direction of travel for the dolly. We do not normalize since it
|
||||||
|
// is desirable to move faster when further away from the targetPosition.
|
||||||
|
vec3 u = grabScene - Base::mEye;
|
||||||
|
|
||||||
|
// Prevent getting stuck when zooming in.
|
||||||
|
if (scrolldelta < 0) {
|
||||||
|
const FLOAT distanceToSurface = length(u);
|
||||||
|
if (distanceToSurface < Base::mProps.zoomSpeed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u *= -scrolldelta * Base::mProps.zoomSpeed;
|
||||||
|
|
||||||
|
const vec3 eyePosition = Base::mEye + u;
|
||||||
|
const vec3 targetPosition = Base::mTarget + u;
|
||||||
|
moveWithConstraints(eyePosition, targetPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getCurrentBookmark() const override {
|
||||||
|
const vec3 dir = normalize(Base::mTarget - Base::mEye);
|
||||||
|
|
||||||
|
FLOAT distance;
|
||||||
|
raycastPlane(Base::mEye, dir, &distance);
|
||||||
|
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT halfExtent = distance * tan(fov / 2.0);
|
||||||
|
|
||||||
|
vec3 targetPosition = Base::mEye + dir * distance;
|
||||||
|
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
|
||||||
|
const vec3 vvec = cross(targetToEye, uvec);
|
||||||
|
const vec3 centerToTarget = targetPosition - Base::mProps.targetPosition;
|
||||||
|
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::MAP;
|
||||||
|
bookmark.map.extent = halfExtent * 2.0;
|
||||||
|
bookmark.map.center.x = dot(uvec, centerToTarget);
|
||||||
|
bookmark.map.center.y = dot(vvec, centerToTarget);
|
||||||
|
|
||||||
|
bookmark.orbit.theta = 0;
|
||||||
|
bookmark.orbit.phi = 0;
|
||||||
|
bookmark.orbit.pivot = Base::mProps.targetPosition +
|
||||||
|
uvec * bookmark.map.center.x +
|
||||||
|
vvec * bookmark.map.center.y;
|
||||||
|
bookmark.orbit.distance = halfExtent / tan(fov / 2.0);
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getHomeBookmark() const override {
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT width = Base::mProps.mapExtent.x;
|
||||||
|
const FLOAT height = Base::mProps.mapExtent.y;
|
||||||
|
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
|
||||||
|
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::MAP;
|
||||||
|
bookmark.map.extent = horiz ? width : height;
|
||||||
|
bookmark.map.center.x = 0;
|
||||||
|
bookmark.map.center.y = 0;
|
||||||
|
|
||||||
|
bookmark.orbit.theta = 0;
|
||||||
|
bookmark.orbit.phi = 0;
|
||||||
|
bookmark.orbit.pivot = Base::mTarget;
|
||||||
|
bookmark.orbit.distance = 0.5 * bookmark.map.extent / tan(fov / 2.0);
|
||||||
|
|
||||||
|
// TODO: Add optional boundary constraints here.
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpToBookmark(const Bookmark& bookmark) override {
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const FLOAT halfExtent = bookmark.map.extent / 2.0;
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT distance = halfExtent / tan(fov / 2.0);
|
||||||
|
vec3 uvec = cross(Base::mProps.upVector, targetToEye);
|
||||||
|
vec3 vvec = cross(targetToEye, uvec);
|
||||||
|
uvec = normalize(uvec) * bookmark.map.center.x;
|
||||||
|
vvec = normalize(vvec) * bookmark.map.center.y;
|
||||||
|
Base::mTarget = Base::mProps.targetPosition + uvec + vvec;
|
||||||
|
Base::mEye = Base::mTarget + distance * targetToEye;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool raycastPlane(const vec3& origin, const vec3& dir, FLOAT* t) const {
|
||||||
|
const vec4 plane = Base::mProps.groundPlane;
|
||||||
|
const vec3 n = vec3(plane[0], plane[1], plane[2]);
|
||||||
|
const vec3 p0 = n * plane[3];
|
||||||
|
const FLOAT denom = -dot(n, dir);
|
||||||
|
if (denom > 1e-6) {
|
||||||
|
const vec3 p0l0 = p0 - origin;
|
||||||
|
*t = dot(p0l0, n) / -denom;
|
||||||
|
return *t >= 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveWithConstraints(vec3 eye, vec3 targetPosition) {
|
||||||
|
Base::mEye = eye;
|
||||||
|
Base::mTarget = targetPosition;
|
||||||
|
// TODO: Add optional boundary constraints here.
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mGrabbing = false;
|
||||||
|
vec3 mGrabScene;
|
||||||
|
vec3 mGrabFar;
|
||||||
|
vec3 mGrabEye;
|
||||||
|
vec3 mGrabTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
|
|
||||||
|
#endif /* CAMUTILS_MAP_MANIPULATOR_H */
|
||||||
201
ios/src/camutils/OrbitManipulator.h
Normal file
201
ios/src/camutils/OrbitManipulator.h
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAMUTILS_ORBIT_MANIPULATOR_H
|
||||||
|
#define CAMUTILS_ORBIT_MANIPULATOR_H
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
|
||||||
|
#define MAX_PHI (F_PI / 2.0 - 0.001)
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
class OrbitManipulator : public Manipulator<FLOAT> {
|
||||||
|
public:
|
||||||
|
using vec2 = filament::math::vec2<FLOAT>;
|
||||||
|
using vec3 = filament::math::vec3<FLOAT>;
|
||||||
|
using vec4 = filament::math::vec4<FLOAT>;
|
||||||
|
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||||
|
using Base = Manipulator<FLOAT>;
|
||||||
|
using Config = typename Base::Config;
|
||||||
|
|
||||||
|
enum GrabState { INACTIVE, ORBITING, PANNING };
|
||||||
|
|
||||||
|
OrbitManipulator(Mode mode, const Config& props) : Base(mode, props) {
|
||||||
|
setProperties(props);
|
||||||
|
Base::mEye = Base::mProps.orbitHomePosition;
|
||||||
|
mPivot = Base::mTarget = Base::mProps.targetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProperties(const Config& props) override {
|
||||||
|
Config resolved = props;
|
||||||
|
|
||||||
|
if (resolved.orbitHomePosition == vec3(0)) {
|
||||||
|
resolved.orbitHomePosition = vec3(0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolved.orbitSpeed == vec2(0)) {
|
||||||
|
resolved.orbitSpeed = vec2(0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, place the ground plane so that it aligns with the targetPosition position.
|
||||||
|
// This is used only when PANNING.
|
||||||
|
if (resolved.groundPlane == vec4(0)) {
|
||||||
|
const FLOAT d = length(resolved.targetPosition);
|
||||||
|
const vec3 n = normalize(resolved.orbitHomePosition - resolved.targetPosition);
|
||||||
|
resolved.groundPlane = vec4(n, -d);
|
||||||
|
}
|
||||||
|
|
||||||
|
Base::setProperties(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabBegin(int x, int y, bool strafe) override {
|
||||||
|
mGrabState = strafe ? PANNING : ORBITING;
|
||||||
|
mGrabPivot = mPivot;
|
||||||
|
mGrabEye = Base::mEye;
|
||||||
|
mGrabTarget = Base::mTarget;
|
||||||
|
mGrabBookmark = getCurrentBookmark();
|
||||||
|
mGrabWinX = x;
|
||||||
|
mGrabWinY = y;
|
||||||
|
mGrabFar = Base::raycastFarPlane(x, y);
|
||||||
|
Base::raycast(x, y, &mGrabScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabUpdate(int x, int y) override {
|
||||||
|
const int delx = mGrabWinX - x;
|
||||||
|
const int dely = mGrabWinY - y;
|
||||||
|
|
||||||
|
if (mGrabState == ORBITING) {
|
||||||
|
Bookmark bookmark = getCurrentBookmark();
|
||||||
|
|
||||||
|
const FLOAT theta = delx * Base::mProps.orbitSpeed.x;
|
||||||
|
const FLOAT phi = dely * Base::mProps.orbitSpeed.y;
|
||||||
|
const FLOAT maxPhi = MAX_PHI;
|
||||||
|
|
||||||
|
bookmark.orbit.phi = clamp(mGrabBookmark.orbit.phi + phi, -maxPhi, +maxPhi);
|
||||||
|
bookmark.orbit.theta = mGrabBookmark.orbit.theta + theta;
|
||||||
|
|
||||||
|
jumpToBookmark(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mGrabState == PANNING) {
|
||||||
|
const FLOAT ulen = distance(mGrabScene, mGrabEye);
|
||||||
|
const FLOAT vlen = distance(mGrabFar, mGrabScene);
|
||||||
|
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
|
||||||
|
mPivot = mGrabPivot + translation;
|
||||||
|
Base::mEye = mGrabEye + translation;
|
||||||
|
Base::mTarget = mGrabTarget + translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabEnd() override {
|
||||||
|
mGrabState = INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(int x, int y, FLOAT scrolldelta) override {
|
||||||
|
const vec3 gaze = normalize(Base::mTarget - Base::mEye);
|
||||||
|
const vec3 movement = gaze * Base::mProps.zoomSpeed * -scrolldelta;
|
||||||
|
const vec3 v0 = mPivot - Base::mEye;
|
||||||
|
Base::mEye += movement;
|
||||||
|
Base::mTarget += movement;
|
||||||
|
const vec3 v1 = mPivot - Base::mEye;
|
||||||
|
|
||||||
|
// Check if the camera has moved past the point of interest.
|
||||||
|
if (dot(v0, v1) < 0) {
|
||||||
|
mFlipped = !mFlipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getCurrentBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::ORBIT;
|
||||||
|
const vec3 pivotToEye = Base::mEye - mPivot;
|
||||||
|
const FLOAT d = length(pivotToEye);
|
||||||
|
const FLOAT x = pivotToEye.x / d;
|
||||||
|
const FLOAT y = pivotToEye.y / d;
|
||||||
|
const FLOAT z = pivotToEye.z / d;
|
||||||
|
|
||||||
|
bookmark.orbit.phi = asin(y);
|
||||||
|
bookmark.orbit.theta = atan2(x, z);
|
||||||
|
bookmark.orbit.distance = mFlipped ? -d : d;
|
||||||
|
bookmark.orbit.pivot = mPivot;
|
||||||
|
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT halfExtent = d * tan(fov / 2.0);
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
|
||||||
|
const vec3 vvec = cross(targetToEye, uvec);
|
||||||
|
const vec3 centerToTarget = mPivot - Base::mProps.targetPosition;
|
||||||
|
|
||||||
|
bookmark.map.extent = halfExtent * 2;
|
||||||
|
bookmark.map.center.x = dot(uvec, centerToTarget);
|
||||||
|
bookmark.map.center.y = dot(vvec, centerToTarget);
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getHomeBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::ORBIT;
|
||||||
|
bookmark.orbit.phi = FLOAT(0);
|
||||||
|
bookmark.orbit.theta = FLOAT(0);
|
||||||
|
bookmark.orbit.pivot = Base::mProps.targetPosition;
|
||||||
|
bookmark.orbit.distance = distance(Base::mProps.targetPosition, Base::mProps.orbitHomePosition);
|
||||||
|
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT halfExtent = bookmark.orbit.distance * tan(fov / 2.0);
|
||||||
|
|
||||||
|
bookmark.map.extent = halfExtent * 2;
|
||||||
|
bookmark.map.center.x = 0;
|
||||||
|
bookmark.map.center.y = 0;
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpToBookmark(const Bookmark& bookmark) override {
|
||||||
|
mPivot = bookmark.orbit.pivot;
|
||||||
|
const FLOAT x = sin(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
|
||||||
|
const FLOAT y = sin(bookmark.orbit.phi);
|
||||||
|
const FLOAT z = cos(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
|
||||||
|
Base::mEye = mPivot + vec3(x, y, z) * abs(bookmark.orbit.distance);
|
||||||
|
mFlipped = bookmark.orbit.distance < 0;
|
||||||
|
Base::mTarget = Base::mEye + vec3(x, y, z) * (mFlipped ? 1.0 : -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GrabState mGrabState = INACTIVE;
|
||||||
|
bool mFlipped = false;
|
||||||
|
vec3 mGrabPivot;
|
||||||
|
vec3 mGrabScene;
|
||||||
|
vec3 mGrabFar;
|
||||||
|
vec3 mGrabEye;
|
||||||
|
vec3 mGrabTarget;
|
||||||
|
Bookmark mGrabBookmark;
|
||||||
|
int mGrabWinX;
|
||||||
|
int mGrabWinY;
|
||||||
|
vec3 mPivot;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
|
|
||||||
|
#endif /* CAMUTILS_ORBIT_MANIPULATOR_H */
|
||||||
@@ -197,7 +197,7 @@ abstract class FilamentController {
|
|||||||
///
|
///
|
||||||
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
||||||
///
|
///
|
||||||
Future zoomUpdate(double z);
|
Future zoomUpdate(double x, double y, double z);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
|
||||||
|
|||||||
@@ -191,16 +191,45 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
_isReadyForScene.complete(true);
|
_isReadyForScene.complete(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// I'm not exactly sure how to resize the backing textures on all platforms.
|
||||||
|
/// So for now, I'm sticking with the safe option when the widget is resized: destroying the swapchain, recreating the textures, and creating a new swapchain.
|
||||||
|
///
|
||||||
@override
|
@override
|
||||||
Future resize(int width, int height, {double scaleFactor = 1.0}) async {
|
Future resize(int width, int height, {double scaleFactor = 1.0}) async {
|
||||||
_resizing = true;
|
_resizing = true;
|
||||||
setRendering(false);
|
setRendering(false);
|
||||||
|
_lib.destroy_swap_chain(_viewer!);
|
||||||
|
await destroyTexture();
|
||||||
size = ui.Size(width * _pixelRatio, height * _pixelRatio);
|
size = ui.Size(width * _pixelRatio, height * _pixelRatio);
|
||||||
_textureId = await _channel
|
|
||||||
.invokeMethod("resize", [size.width, size.height, scaleFactor]);
|
var textures =
|
||||||
_textureIdController.add(_textureId);
|
await _channel.invokeMethod("createTexture", [size.width, size.height]);
|
||||||
|
var flutterTextureId = textures[0];
|
||||||
|
_textureId = flutterTextureId;
|
||||||
|
|
||||||
|
// void* on iOS (pointer to pixel buffer), void* on Android (pointer to native window), null on Windows/macOS
|
||||||
|
var surfaceAddress = textures[1] as int? ?? 0;
|
||||||
|
|
||||||
|
// null on iOS/Android, void* on MacOS (pointer to metal texture), GLuid on Windows/Linux
|
||||||
|
var nativeTexture = textures[2] as int? ?? 0;
|
||||||
|
|
||||||
|
_lib.create_swap_chain_ffi(
|
||||||
|
_viewer!,
|
||||||
|
Pointer<Void>.fromAddress(surfaceAddress),
|
||||||
|
size.width.toInt(),
|
||||||
|
size.height.toInt());
|
||||||
|
if (nativeTexture != 0) {
|
||||||
|
assert(surfaceAddress == 0);
|
||||||
|
print("Creating render target from native texture $nativeTexture");
|
||||||
|
_lib.create_render_target_ffi(
|
||||||
|
_viewer!, nativeTexture, size.width.toInt(), size.height.toInt());
|
||||||
|
}
|
||||||
|
|
||||||
_lib.update_viewport_and_camera_projection_ffi(
|
_lib.update_viewport_and_camera_projection_ffi(
|
||||||
_viewer!, size.width.toInt(), size.height.toInt(), scaleFactor);
|
_viewer!, size.width.toInt(), size.height.toInt(), 1.0);
|
||||||
|
|
||||||
|
_textureIdController.add(_textureId);
|
||||||
_resizing = false;
|
_resizing = false;
|
||||||
setRendering(true);
|
setRendering(true);
|
||||||
}
|
}
|
||||||
@@ -553,11 +582,11 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future zoomUpdate(double z) async {
|
Future zoomUpdate(double x, double y, double z) async {
|
||||||
if (_viewer == null || _resizing) {
|
if (_viewer == null || _resizing) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
_lib.scroll_update(_viewer!, 0.0, 0.0, z);
|
_lib.scroll_update(_viewer!, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -434,11 +434,11 @@ class FilamentControllerMethodChannel extends FilamentController {
|
|||||||
await _channel.invokeMethod("scrollBegin");
|
await _channel.invokeMethod("scrollBegin");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future zoomUpdate(double z) async {
|
Future zoomUpdate(double x, double y, double z) async {
|
||||||
if (_viewer == null || _resizing) {
|
if (_viewer == null || _resizing) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
await _channel.invokeMethod("scrollUpdate", [0.0, 0.0, z]);
|
await _channel.invokeMethod("scrollUpdate", [x, y, z]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future zoomEnd() async {
|
Future zoomEnd() async {
|
||||||
|
|||||||
@@ -38,15 +38,12 @@ class FilamentGestureDetector extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
final bool listenerEnabled;
|
final bool listenerEnabled;
|
||||||
|
|
||||||
final double zoomDelta;
|
|
||||||
|
|
||||||
const FilamentGestureDetector(
|
const FilamentGestureDetector(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.child,
|
this.child,
|
||||||
this.showControlOverlay = false,
|
this.showControlOverlay = false,
|
||||||
this.listenerEnabled = true,
|
this.listenerEnabled = true})
|
||||||
this.zoomDelta = 1})
|
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -32,15 +32,12 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
final bool listenerEnabled;
|
final bool listenerEnabled;
|
||||||
|
|
||||||
final double zoomDelta;
|
|
||||||
|
|
||||||
const FilamentGestureDetectorDesktop(
|
const FilamentGestureDetectorDesktop(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.child,
|
this.child,
|
||||||
this.showControlOverlay = false,
|
this.showControlOverlay = false,
|
||||||
this.listenerEnabled = true,
|
this.listenerEnabled = true})
|
||||||
this.zoomDelta = 1})
|
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -71,15 +68,18 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
///
|
///
|
||||||
/// Scroll-wheel on desktop, interpreted as zoom
|
/// Scroll-wheel on desktop, interpreted as zoom
|
||||||
///
|
///
|
||||||
void _zoom(PointerScrollEvent pointerSignal) {
|
void _zoom(PointerScrollEvent pointerSignal) async {
|
||||||
_scrollTimer?.cancel();
|
_scrollTimer?.cancel();
|
||||||
widget.controller.zoomBegin();
|
await widget.controller.zoomBegin();
|
||||||
widget.controller.zoomUpdate(pointerSignal.scrollDelta.dy > 0
|
await widget.controller.zoomUpdate(
|
||||||
? widget.zoomDelta
|
pointerSignal.localPosition.dx,
|
||||||
: -widget.zoomDelta);
|
pointerSignal.localPosition.dy,
|
||||||
_scrollTimer = Timer(const Duration(milliseconds: 100), () {
|
pointerSignal.scrollDelta.dy > 0 ? 1 : -1);
|
||||||
widget.controller.zoomEnd();
|
|
||||||
_scrollTimer = null;
|
// we don't want to end the zoom in the same frame, because this will destroy the camera manipulator (and cancel the zoom update).
|
||||||
|
// here, we just defer calling [zoomEnd] for 100ms to ensure the update is propagated through.
|
||||||
|
_scrollTimer = Timer(Duration(milliseconds: 100), () async {
|
||||||
|
await widget.controller.zoomEnd();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,8 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
|
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
|
||||||
if (!_pointerMoving) {
|
if (!_pointerMoving) {
|
||||||
if (d.buttons == kTertiaryButton) {
|
if (d.buttons == kTertiaryButton) {
|
||||||
widget.controller.rotateStart(d.position.dx, d.position.dy);
|
widget.controller
|
||||||
|
.rotateStart(d.localPosition.dx, d.localPosition.dy);
|
||||||
} else {
|
} else {
|
||||||
widget.controller
|
widget.controller
|
||||||
.panStart(d.localPosition.dx, d.localPosition.dy);
|
.panStart(d.localPosition.dx, d.localPosition.dy);
|
||||||
@@ -117,7 +118,8 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
// set the _pointerMoving flag so we don't call rotateStart/panStart on future move events
|
// set the _pointerMoving flag so we don't call rotateStart/panStart on future move events
|
||||||
_pointerMoving = true;
|
_pointerMoving = true;
|
||||||
if (d.buttons == kTertiaryButton) {
|
if (d.buttons == kTertiaryButton) {
|
||||||
widget.controller.rotateUpdate(d.position.dx, d.position.dy);
|
widget.controller
|
||||||
|
.rotateUpdate(d.localPosition.dx, d.localPosition.dy);
|
||||||
} else {
|
} else {
|
||||||
widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy);
|
widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,8 +158,8 @@ class _FilamentGestureDetectorMobileState
|
|||||||
},
|
},
|
||||||
onScaleUpdate: (ScaleUpdateDetails d) async {
|
onScaleUpdate: (ScaleUpdateDetails d) async {
|
||||||
if (d.pointerCount == 2) {
|
if (d.pointerCount == 2) {
|
||||||
widget.controller
|
widget.controller.zoomUpdate(d.localFocalPoint.dx,
|
||||||
.zoomUpdate(d.horizontalScale > 1 ? 0.1 : -0.1);
|
d.localFocalPoint.dy, d.horizontalScale > 1 ? 0.1 : -0.1);
|
||||||
} else if (!_scaling) {
|
} else if (!_scaling) {
|
||||||
if (_rotateOnPointerMove) {
|
if (_rotateOnPointerMove) {
|
||||||
widget.controller
|
widget.controller
|
||||||
|
|||||||
@@ -64,8 +64,10 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
|
|||||||
|
|
||||||
var markTextureFrameAvailable : @convention(c) (UnsafeMutableRawPointer?) -> () = { instancePtr in
|
var markTextureFrameAvailable : @convention(c) (UnsafeMutableRawPointer?) -> () = { instancePtr in
|
||||||
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(instancePtr!).takeUnretainedValue()
|
let instance:SwiftPolyvoxFilamentPlugin = Unmanaged<SwiftPolyvoxFilamentPlugin>.fromOpaque(instancePtr!).takeUnretainedValue()
|
||||||
|
if(instance.flutterTextureId != nil) {
|
||||||
instance.registry.textureFrameAvailable(instance.flutterTextureId!)
|
instance.registry.textureFrameAvailable(instance.flutterTextureId!)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
|
public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
|
||||||
if(pixelBuffer == nil) {
|
if(pixelBuffer == nil) {
|
||||||
@@ -142,6 +144,10 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
|
|||||||
Int(width), Int(height),
|
Int(width), Int(height),
|
||||||
0,
|
0,
|
||||||
&cvMetalTexture);
|
&cvMetalTexture);
|
||||||
|
if(cvret != 0) {
|
||||||
|
result(FlutterError())
|
||||||
|
return
|
||||||
|
}
|
||||||
metalTexture = CVMetalTextureGetTexture(cvMetalTexture!);
|
metalTexture = CVMetalTextureGetTexture(cvMetalTexture!);
|
||||||
let pixelBufferPtr = CVPixelBufferGetBaseAddress(pixelBuffer!);
|
let pixelBufferPtr = CVPixelBufferGetBaseAddress(pixelBuffer!);
|
||||||
let pixelBufferAddress = Int(bitPattern:pixelBufferPtr);
|
let pixelBufferAddress = Int(bitPattern:pixelBufferPtr);
|
||||||
@@ -160,13 +166,7 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
|
|||||||
let args = call.arguments as! [Any]
|
let args = call.arguments as! [Any]
|
||||||
let width = UInt32(args[0] as! Int64)
|
let width = UInt32(args[0] as! Int64)
|
||||||
let height = UInt32(args[1] as! Int64)
|
let height = UInt32(args[1] as! Int64)
|
||||||
if(self.flutterTextureId != nil) {
|
result(FlutterMethodNotImplemented)
|
||||||
self.registry.unregisterTexture(self.flutterTextureId!)
|
|
||||||
}
|
|
||||||
createPixelBuffer(width: Int(width), height:Int(height))
|
|
||||||
let metalTextureId = Int(bitPattern:Unmanaged.passUnretained(metalTexture!).toOpaque())
|
|
||||||
print("Resized to \(args[0])x\(args[1])")
|
|
||||||
result(self.flutterTextureId);
|
|
||||||
default:
|
default:
|
||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ namespace polyvox {
|
|||||||
void loadPngTexture(string path, ResourceBuffer data);
|
void loadPngTexture(string path, ResourceBuffer data);
|
||||||
void loadTextureFromPath(string path);
|
void loadTextureFromPath(string path);
|
||||||
|
|
||||||
|
Manipulator<double>* _manipulator = nullptr;
|
||||||
void _createManipulator();
|
void _createManipulator();
|
||||||
uint32_t _lastFrameTimeInNanos;
|
uint32_t _lastFrameTimeInNanos;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ A new Flutter plugin project.
|
|||||||
s.author = { 'Your Company' => 'email@example.com' }
|
s.author = { 'Your Company' => 'email@example.com' }
|
||||||
|
|
||||||
s.source = { :path => '.' }
|
s.source = { :path => '.' }
|
||||||
s.source_files = 'Classes/*', 'src/*', 'include/filament/*', 'include/*', 'include/material/*.c'
|
s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'include/filament/*', 'include/*', 'include/material/*.c'
|
||||||
s.public_header_files = 'include/SwiftPolyvoxFilamentPlugin-Bridging-Header.h', 'include/PolyvoxFilamentApi.h', 'include/PolyvoxFilamentFFIApi.h', 'include/ResourceBuffer.hpp' #, 'include/filament/*'
|
s.public_header_files = 'include/SwiftPolyvoxFilamentPlugin-Bridging-Header.h', 'include/PolyvoxFilamentApi.h', 'include/PolyvoxFilamentFFIApi.h', 'include/ResourceBuffer.hpp' #, 'include/filament/*'
|
||||||
s.dependency 'FlutterMacOS'
|
s.dependency 'FlutterMacOS'
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ A new Flutter plugin project.
|
|||||||
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
|
||||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/include" "${PODS_TARGET_SRCROOT}/include/filament" "$(inherited)"',
|
'USER_HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/include" "${PODS_TARGET_SRCROOT}/include/filament" "$(inherited)"',
|
||||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||||
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lcamutils -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lvkshaders -lbluegl -lbluevk -lbasis_transcoder -lmeshoptimizer',
|
"OTHER_LDFLAGS" => '-lfilament -lbackend -lfilameshio -lviewer -lfilamat -lgeometry -lutils -lfilabridge -lgltfio_core -lfilament-iblprefilter -limage -limageio -ltinyexr -lgltfio_core -lfilaflat -ldracodec -libl -lktxreader -lpng -lz -lstb -luberzlib -lsmol-v -luberarchive -lzstd -lvkshaders -lbluegl -lbluevk -lbasis_transcoder -lmeshoptimizer',
|
||||||
'LIBRARY_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/lib" "$(inherited)"',
|
'LIBRARY_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/lib" "$(inherited)"',
|
||||||
}
|
}
|
||||||
s.swift_version = '5.0'
|
s.swift_version = '5.0'
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include <filament/Camera.h>
|
#include <filament/Camera.h>
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/platforms/OpenGLPlatform.h>
|
#include <backend/platforms/OpenGLPlatform.h>
|
||||||
#include <filament/ColorGrading.h>
|
#include <filament/ColorGrading.h>
|
||||||
@@ -86,12 +87,14 @@ using namespace gltfio;
|
|||||||
using namespace utils;
|
using namespace utils;
|
||||||
using namespace image;
|
using namespace image;
|
||||||
|
|
||||||
namespace filament {
|
namespace filament
|
||||||
|
{
|
||||||
class IndirectLight;
|
class IndirectLight;
|
||||||
class LightManager;
|
class LightManager;
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|
||||||
namespace polyvox {
|
namespace polyvox
|
||||||
|
{
|
||||||
|
|
||||||
const double kNearPlane = 0.05; // 5 cm
|
const double kNearPlane = 0.05; // 5 cm
|
||||||
const double kFarPlane = 1000.0; // 1 km
|
const double kFarPlane = 1000.0; // 1 km
|
||||||
@@ -99,7 +102,8 @@ const double kFarPlane = 1000.0; // 1 km
|
|||||||
// const float kAperture = 1.0f;
|
// const float kAperture = 1.0f;
|
||||||
// const float kShutterSpeed = 1.0f;
|
// const float kShutterSpeed = 1.0f;
|
||||||
// const float kSensitivity = 50.0f;
|
// const float kSensitivity = 50.0f;
|
||||||
struct Vertex {
|
struct Vertex
|
||||||
|
{
|
||||||
filament::math::float2 position;
|
filament::math::float2 position;
|
||||||
uint32_t color;
|
uint32_t color;
|
||||||
};
|
};
|
||||||
@@ -112,7 +116,8 @@ static constexpr float4 sFullScreenTriangleVertices[3] = {
|
|||||||
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
|
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
|
||||||
|
|
||||||
FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapper *const resourceLoaderWrapper, void *const platform, const char *uberArchivePath)
|
FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapper *const resourceLoaderWrapper, void *const platform, const char *uberArchivePath)
|
||||||
: _resourceLoaderWrapper(resourceLoaderWrapper) {
|
: _resourceLoaderWrapper(resourceLoaderWrapper)
|
||||||
|
{
|
||||||
|
|
||||||
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
||||||
|
|
||||||
@@ -193,8 +198,7 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
_ncm,
|
_ncm,
|
||||||
_engine,
|
_engine,
|
||||||
_scene,
|
_scene,
|
||||||
uberArchivePath
|
uberArchivePath);
|
||||||
);
|
|
||||||
|
|
||||||
_imageTexture = Texture::Builder()
|
_imageTexture = Texture::Builder()
|
||||||
.width(1)
|
.width(1)
|
||||||
@@ -203,7 +207,8 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
.format(Texture::InternalFormat::RGB16F)
|
.format(Texture::InternalFormat::RGB16F)
|
||||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||||
.build(*_engine);
|
.build(*_engine);
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
_imageMaterial =
|
_imageMaterial =
|
||||||
Material::Builder()
|
Material::Builder()
|
||||||
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
|
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
|
||||||
@@ -211,7 +216,9 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||||
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f));
|
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||||
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
||||||
} catch(...) {
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
Log("Failed to load background image material provider");
|
Log("Failed to load background image material provider");
|
||||||
std::rethrow_exception(std::current_exception());
|
std::rethrow_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
@@ -250,21 +257,25 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr
|
|||||||
_scene->addEntity(imageEntity);
|
_scene->addEntity(imageEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setPostProcessing(bool enabled) {
|
void FilamentViewer::setPostProcessing(bool enabled)
|
||||||
|
{
|
||||||
_view->setPostProcessingEnabled(enabled);
|
_view->setPostProcessingEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setBloom(float strength) {
|
void FilamentViewer::setBloom(float strength)
|
||||||
|
{
|
||||||
decltype(_view->getBloomOptions()) opts;
|
decltype(_view->getBloomOptions()) opts;
|
||||||
opts.enabled = true;
|
opts.enabled = true;
|
||||||
opts.strength = strength;
|
opts.strength = strength;
|
||||||
_view->setBloomOptions(opts);
|
_view->setBloomOptions(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setToneMapping(ToneMapping toneMapping) {
|
void FilamentViewer::setToneMapping(ToneMapping toneMapping)
|
||||||
|
{
|
||||||
|
|
||||||
ToneMapper *tm;
|
ToneMapper *tm;
|
||||||
switch(toneMapping) {
|
switch (toneMapping)
|
||||||
|
{
|
||||||
case ToneMapping::ACES:
|
case ToneMapping::ACES:
|
||||||
Log("Setting tone mapping to ACES");
|
Log("Setting tone mapping to ACES");
|
||||||
tm = new ACESToneMapper();
|
tm = new ACESToneMapper();
|
||||||
@@ -282,21 +293,22 @@ void FilamentViewer::setToneMapping(ToneMapping toneMapping) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto newColorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine);
|
auto newColorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine);
|
||||||
_view->setColorGrading(newColorGrading);
|
_view->setColorGrading(newColorGrading);
|
||||||
_engine->destroy(colorGrading);
|
_engine->destroy(colorGrading);
|
||||||
delete tm;
|
delete tm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setFrameInterval(float frameInterval) {
|
void FilamentViewer::setFrameInterval(float frameInterval)
|
||||||
|
{
|
||||||
Renderer::FrameRateOptions fro;
|
Renderer::FrameRateOptions fro;
|
||||||
fro.interval = frameInterval;
|
fro.interval = frameInterval;
|
||||||
_renderer->setFrameRateOptions(fro);
|
_renderer->setFrameRateOptions(fro);
|
||||||
Log("Set framerate interval to %f", frameInterval);
|
Log("Set framerate interval to %f", frameInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) {
|
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows)
|
||||||
|
{
|
||||||
auto light = EntityManager::get().create();
|
auto light = EntityManager::get().create();
|
||||||
LightManager::Builder(t)
|
LightManager::Builder(t)
|
||||||
.color(Color::cct(colour))
|
.color(Color::cct(colour))
|
||||||
@@ -312,30 +324,37 @@ int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float inten
|
|||||||
return entityId;
|
return entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeLight(EntityId entityId) {
|
void FilamentViewer::removeLight(EntityId entityId)
|
||||||
|
{
|
||||||
Log("Removing light with entity ID %d", entityId);
|
Log("Removing light with entity ID %d", entityId);
|
||||||
auto entity = utils::Entity::import(entityId);
|
auto entity = utils::Entity::import(entityId);
|
||||||
if(entity.isNull()) {
|
if (entity.isNull())
|
||||||
|
{
|
||||||
Log("Error: light entity not found under ID %d", entityId);
|
Log("Error: light entity not found under ID %d", entityId);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
remove(_lights.begin(), _lights.end(), entity);
|
remove(_lights.begin(), _lights.end(), entity);
|
||||||
_scene->remove(entity);
|
_scene->remove(entity);
|
||||||
EntityManager::get().destroy(1, &entity);
|
EntityManager::get().destroy(1, &entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::clearLights() {
|
void FilamentViewer::clearLights()
|
||||||
|
{
|
||||||
Log("Removing all lights");
|
Log("Removing all lights");
|
||||||
_scene->removeEntities(_lights.data(), _lights.size());
|
_scene->removeEntities(_lights.data(), _lights.size());
|
||||||
EntityManager::get().destroy(_lights.size(), _lights.data());
|
EntityManager::get().destroy(_lights.size(), _lights.data());
|
||||||
_lights.clear();
|
_lights.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool endsWith(string path, string ending) {
|
static bool endsWith(string path, string ending)
|
||||||
|
{
|
||||||
return path.compare(path.length() - ending.length(), ending.length(), ending) == 0;
|
return path.compare(path.length() - ending.length(), ending.length(), ending) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadKtx2Texture(string path, ResourceBuffer rb) {
|
void FilamentViewer::loadKtx2Texture(string path, ResourceBuffer rb)
|
||||||
|
{
|
||||||
|
|
||||||
// TODO - check all this
|
// TODO - check all this
|
||||||
|
|
||||||
@@ -356,34 +375,36 @@ void FilamentViewer::loadKtx2Texture(string path, ResourceBuffer rb) {
|
|||||||
// ktxreader::Ktx2Reader::TransferFunction::LINEAR);
|
// ktxreader::Ktx2Reader::TransferFunction::LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadKtxTexture(string path, ResourceBuffer rb) {
|
void FilamentViewer::loadKtxTexture(string path, ResourceBuffer rb)
|
||||||
|
{
|
||||||
ktxreader::Ktx1Bundle *bundle =
|
ktxreader::Ktx1Bundle *bundle =
|
||||||
new ktxreader::Ktx1Bundle(static_cast<const uint8_t *>(rb.data),
|
new ktxreader::Ktx1Bundle(static_cast<const uint8_t *>(rb.data),
|
||||||
static_cast<uint32_t>(rb.size));
|
static_cast<uint32_t>(rb.size));
|
||||||
|
|
||||||
|
|
||||||
// because the ResourceBuffer will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
// because the ResourceBuffer will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
||||||
ResourceBuffer *rbCopy = new ResourceBuffer(rb);
|
ResourceBuffer *rbCopy = new ResourceBuffer(rb);
|
||||||
|
|
||||||
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, rbCopy};
|
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, rbCopy};
|
||||||
|
|
||||||
|
|
||||||
_imageTexture =
|
_imageTexture =
|
||||||
ktxreader::Ktx1Reader::createTexture(_engine, *bundle, false, [](void* userdata) {
|
ktxreader::Ktx1Reader::createTexture(
|
||||||
|
_engine, *bundle, false, [](void *userdata)
|
||||||
|
{
|
||||||
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
||||||
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
||||||
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
||||||
loader->free(*rb);
|
loader->free(*rb);
|
||||||
delete rb;
|
delete rb;
|
||||||
delete vec;
|
delete vec; },
|
||||||
}, callbackData);
|
callbackData);
|
||||||
|
|
||||||
auto info = bundle->getInfo();
|
auto info = bundle->getInfo();
|
||||||
_imageWidth = info.pixelWidth;
|
_imageWidth = info.pixelWidth;
|
||||||
_imageHeight = info.pixelHeight;
|
_imageHeight = info.pixelHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb)
|
||||||
|
{
|
||||||
|
|
||||||
polyvox::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size);
|
polyvox::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size);
|
||||||
|
|
||||||
@@ -392,7 +413,8 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
|||||||
LinearImage *image = new LinearImage(ImageDecoder::decode(
|
LinearImage *image = new LinearImage(ImageDecoder::decode(
|
||||||
inputStream, path.c_str(), ImageDecoder::ColorSpace::SRGB));
|
inputStream, path.c_str(), ImageDecoder::ColorSpace::SRGB));
|
||||||
|
|
||||||
if (!image->isValid()) {
|
if (!image->isValid())
|
||||||
|
{
|
||||||
Log("Invalid image : %s", path.c_str());
|
Log("Invalid image : %s", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -411,7 +433,8 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
|||||||
.build(*_engine);
|
.build(*_engine);
|
||||||
|
|
||||||
Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t,
|
Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t,
|
||||||
void *data) {
|
void *data)
|
||||||
|
{
|
||||||
delete reinterpret_cast<LinearImage *>(data);
|
delete reinterpret_cast<LinearImage *>(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -426,43 +449,54 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
|||||||
_resourceLoaderWrapper->free(rb);
|
_resourceLoaderWrapper->free(rb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadTextureFromPath(string path) {
|
void FilamentViewer::loadTextureFromPath(string path)
|
||||||
|
{
|
||||||
string ktxExt(".ktx");
|
string ktxExt(".ktx");
|
||||||
string ktx2Ext(".ktx2");
|
string ktx2Ext(".ktx2");
|
||||||
string pngExt(".png");
|
string pngExt(".png");
|
||||||
|
|
||||||
if (path.length() < 5) {
|
if (path.length() < 5)
|
||||||
|
{
|
||||||
Log("Invalid resource path : %s", path.c_str());
|
Log("Invalid resource path : %s", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceBuffer rb = _resourceLoaderWrapper->load(path.c_str());
|
ResourceBuffer rb = _resourceLoaderWrapper->load(path.c_str());
|
||||||
|
|
||||||
if(endsWith(path, ktxExt)) {
|
if (endsWith(path, ktxExt))
|
||||||
|
{
|
||||||
loadKtxTexture(path, rb);
|
loadKtxTexture(path, rb);
|
||||||
} else if(endsWith(path, ktx2Ext)) {
|
}
|
||||||
|
else if (endsWith(path, ktx2Ext))
|
||||||
|
{
|
||||||
loadKtx2Texture(path, rb);
|
loadKtx2Texture(path, rb);
|
||||||
} else if(endsWith(path, pngExt)) {
|
}
|
||||||
|
else if (endsWith(path, pngExt))
|
||||||
|
{
|
||||||
loadPngTexture(path, rb);
|
loadPngTexture(path, rb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a) {
|
void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a)
|
||||||
|
{
|
||||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||||
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a));
|
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a));
|
||||||
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::clearBackgroundImage() {
|
void FilamentViewer::clearBackgroundImage()
|
||||||
|
{
|
||||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||||
if (_imageTexture) {
|
if (_imageTexture)
|
||||||
|
{
|
||||||
_engine->destroy(_imageTexture);
|
_engine->destroy(_imageTexture);
|
||||||
_imageTexture = nullptr;
|
_imageTexture = nullptr;
|
||||||
Log("Destroyed background image texture");
|
Log("Destroyed background image texture");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeight) {
|
void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeight)
|
||||||
|
{
|
||||||
|
|
||||||
string resourcePathString(resourcePath);
|
string resourcePathString(resourcePath);
|
||||||
|
|
||||||
@@ -477,13 +511,15 @@ void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeigh
|
|||||||
const Viewport &vp = _view->getViewport();
|
const Viewport &vp = _view->getViewport();
|
||||||
Log("Image width %d height %d vp width %d height %d", _imageWidth, _imageHeight, vp.width, vp.height);
|
Log("Image width %d height %d vp width %d height %d", _imageWidth, _imageHeight, vp.width, vp.height);
|
||||||
|
|
||||||
|
|
||||||
float xScale = float(vp.width) / float(_imageWidth);
|
float xScale = float(vp.width) / float(_imageWidth);
|
||||||
|
|
||||||
float yScale;
|
float yScale;
|
||||||
if(fillHeight) {
|
if (fillHeight)
|
||||||
|
{
|
||||||
yScale = 1.0f;
|
yScale = 1.0f;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
yScale = float(vp.height) / float(_imageHeight);
|
yScale = float(vp.height) / float(_imageHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,17 +528,16 @@ void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeigh
|
|||||||
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
||||||
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
||||||
_imageMaterial->setDefaultParameter("showImage", 1);
|
_imageMaterial->setDefaultParameter("showImage", 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Translates the background image by (x,y) pixels.
|
/// Translates the background image by (x,y) pixels.
|
||||||
/// If clamp is true, x/y are both clamped so that the left/top and right/bottom sides of the background image
|
/// If clamp is true, x/y are both clamped so that the left/top and right/bottom sides of the background image
|
||||||
/// are positioned at a max/min of -1/1 respectively
|
/// are positioned at a max/min of -1/1 respectively
|
||||||
/// (i.e. you cannot set a position where the left/top or right/bottom sides would be "inside" the screen coordinate space).
|
/// (i.e. you cannot set a position where the left/top or right/bottom sides would be "inside" the screen coordinate space).
|
||||||
///
|
///
|
||||||
void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=false) {
|
void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp = false)
|
||||||
|
{
|
||||||
|
|
||||||
// to translate the background image, we apply a transform to the UV coordinates of the quad texture, not the quad itself (see image.mat).
|
// to translate the background image, we apply a transform to the UV coordinates of the quad texture, not the quad itself (see image.mat).
|
||||||
// this allows us to set a background colour for the quad when the texture has been translated outside the quad's bounds.
|
// this allows us to set a background colour for the quad when the texture has been translated outside the quad's bounds.
|
||||||
@@ -517,7 +552,8 @@ void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=fal
|
|||||||
// y *= _imageScale[1][1];
|
// y *= _imageScale[1][1];
|
||||||
|
|
||||||
// TODO - I haven't updated the clamp calculations to work with scaled image width/height percentages so the below code is probably wrong, don't use it until it's fixed.
|
// TODO - I haven't updated the clamp calculations to work with scaled image width/height percentages so the below code is probably wrong, don't use it until it's fixed.
|
||||||
if(clamp) {
|
if (clamp)
|
||||||
|
{
|
||||||
Log("Clamping background image translation");
|
Log("Clamping background image translation");
|
||||||
// first, clamp x/y
|
// first, clamp x/y
|
||||||
auto xScale = float(_imageWidth) / _view->getViewport().width;
|
auto xScale = float(_imageWidth) / _view->getViewport().width;
|
||||||
@@ -530,20 +566,26 @@ void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=fal
|
|||||||
|
|
||||||
// we need to clamp x so that it can only be translated between (left side touching viewport left) and (right side touching viewport right)
|
// we need to clamp x so that it can only be translated between (left side touching viewport left) and (right side touching viewport right)
|
||||||
// if width is less than viewport, these values are 0/1-xScale respectively
|
// if width is less than viewport, these values are 0/1-xScale respectively
|
||||||
if(xScale < 1) {
|
if (xScale < 1)
|
||||||
|
{
|
||||||
xMin = 0;
|
xMin = 0;
|
||||||
xMax = 1 - xScale;
|
xMax = 1 - xScale;
|
||||||
// otherwise, these value are (xScale-1 and 1-xScale)
|
// otherwise, these value are (xScale-1 and 1-xScale)
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
xMin = 1 - xScale;
|
xMin = 1 - xScale;
|
||||||
xMax = 0;
|
xMax = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do the same for y
|
// do the same for y
|
||||||
if(yScale < 1) {
|
if (yScale < 1)
|
||||||
|
{
|
||||||
yMin = 0;
|
yMin = 0;
|
||||||
yMax = 1 - yScale;
|
yMax = 1 - yScale;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
yMin = 1 - yScale;
|
yMin = 1 - yScale;
|
||||||
yMax = 0;
|
yMax = 0;
|
||||||
}
|
}
|
||||||
@@ -558,26 +600,27 @@ void FilamentViewer::setBackgroundImagePosition(float x, float y, bool clamp=fal
|
|||||||
y = -y;
|
y = -y;
|
||||||
Log("x %f y %f", x, y);
|
Log("x %f y %f", x, y);
|
||||||
|
|
||||||
Log("imageScale %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", _imageScale[0][0],_imageScale[0][1],_imageScale[0][2], _imageScale[0][3], \
|
Log("imageScale %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", _imageScale[0][0], _imageScale[0][1], _imageScale[0][2], _imageScale[0][3],
|
||||||
_imageScale[1][0],_imageScale[1][1],_imageScale[1][2], _imageScale[1][3],\
|
_imageScale[1][0], _imageScale[1][1], _imageScale[1][2], _imageScale[1][3],
|
||||||
_imageScale[2][0],_imageScale[2][1],_imageScale[2][2], _imageScale[2][3], \
|
_imageScale[2][0], _imageScale[2][1], _imageScale[2][2], _imageScale[2][3],
|
||||||
_imageScale[3][0], _imageScale[3][1], _imageScale[3][2], _imageScale[3][3]);
|
_imageScale[3][0], _imageScale[3][1], _imageScale[3][2], _imageScale[3][3]);
|
||||||
|
|
||||||
auto transform = math::mat4f::translation(math::float3(x, y, 0.0f)) * _imageScale;
|
auto transform = math::mat4f::translation(math::float3(x, y, 0.0f)) * _imageScale;
|
||||||
|
|
||||||
|
Log("transform %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", transform[0][0], transform[0][1], transform[0][2], transform[0][3],
|
||||||
Log("transform %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", transform[0][0],transform[0][1],transform[0][2], transform[0][3], \
|
transform[1][0], transform[1][1], transform[1][2], transform[1][3],
|
||||||
transform[1][0],transform[1][1],transform[1][2], transform[1][3],\
|
transform[2][0], transform[2][1], transform[2][2], transform[2][3],
|
||||||
transform[2][0],transform[2][1],transform[2][2], transform[2][3], \
|
|
||||||
transform[3][0], transform[3][1], transform[3][2], transform[3][3]);
|
transform[3][0], transform[3][1], transform[3][2], transform[3][3]);
|
||||||
_imageMaterial->setDefaultParameter("transform", transform);
|
_imageMaterial->setDefaultParameter("transform", transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
FilamentViewer::~FilamentViewer() {
|
FilamentViewer::~FilamentViewer()
|
||||||
|
{
|
||||||
clearAssets();
|
clearAssets();
|
||||||
delete _assetManager;
|
delete _assetManager;
|
||||||
|
|
||||||
for(auto it : _lights) {
|
for (auto it : _lights)
|
||||||
|
{
|
||||||
_engine->destroy(it);
|
_engine->destroy(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,21 +636,26 @@ FilamentViewer::~FilamentViewer() {
|
|||||||
|
|
||||||
Renderer *FilamentViewer::getRenderer() { return _renderer; }
|
Renderer *FilamentViewer::getRenderer() { return _renderer; }
|
||||||
|
|
||||||
void FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height) {
|
void FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
||||||
#else
|
#else
|
||||||
if(window) {
|
if (window)
|
||||||
|
{
|
||||||
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||||
Log("Created window swapchain.");
|
Log("Created window swapchain.");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Log("Created headless swapchain.");
|
Log("Created headless swapchain.");
|
||||||
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32_t height) {
|
void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
// Create filament textures and render targets (note the color buffer has the import call)
|
// Create filament textures and render targets (note the color buffer has the import call)
|
||||||
_rtColor = filament::Texture::Builder()
|
_rtColor = filament::Texture::Builder()
|
||||||
.width(width)
|
.width(width)
|
||||||
@@ -633,11 +681,12 @@ void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32
|
|||||||
_view->setRenderTarget(_rt);
|
_view->setRenderTarget(_rt);
|
||||||
|
|
||||||
Log("Set render target for texture id %u to %u x %u", texture, width, height);
|
Log("Set render target for texture id %u to %u x %u", texture, width, height);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::destroySwapChain() {
|
void FilamentViewer::destroySwapChain()
|
||||||
if(_rt) {
|
{
|
||||||
|
if (_rt)
|
||||||
|
{
|
||||||
_view->setRenderTarget(nullptr);
|
_view->setRenderTarget(nullptr);
|
||||||
_engine->destroy(_rtDepth);
|
_engine->destroy(_rtDepth);
|
||||||
_engine->destroy(_rtColor);
|
_engine->destroy(_rtColor);
|
||||||
@@ -646,16 +695,19 @@ void FilamentViewer::destroySwapChain() {
|
|||||||
_rtDepth = nullptr;
|
_rtDepth = nullptr;
|
||||||
_rtColor = nullptr;
|
_rtColor = nullptr;
|
||||||
}
|
}
|
||||||
if (_swapChain) {
|
if (_swapChain)
|
||||||
|
{
|
||||||
_engine->destroy(_swapChain);
|
_engine->destroy(_swapChain);
|
||||||
_swapChain = nullptr;
|
_swapChain = nullptr;
|
||||||
Log("Swapchain destroyed.");
|
Log("Swapchain destroyed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::clearAssets() {
|
void FilamentViewer::clearAssets()
|
||||||
|
{
|
||||||
Log("Clearing all assets");
|
Log("Clearing all assets");
|
||||||
if(_mainCamera) {
|
if (_mainCamera)
|
||||||
|
{
|
||||||
_view->setCamera(_mainCamera);
|
_view->setCamera(_mainCamera);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,7 +716,8 @@ void FilamentViewer::clearAssets() {
|
|||||||
Log("Cleared all assets");
|
Log("Cleared all assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeAsset(EntityId asset) {
|
void FilamentViewer::removeAsset(EntityId asset)
|
||||||
|
{
|
||||||
Log("Removing asset from scene");
|
Log("Removing asset from scene");
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
@@ -677,7 +730,8 @@ void FilamentViewer::removeAsset(EntityId asset) {
|
|||||||
///
|
///
|
||||||
/// Set the exposure for the current active camera.
|
/// Set the exposure for the current active camera.
|
||||||
///
|
///
|
||||||
void FilamentViewer::setCameraExposure(float aperture, float shutterSpeed, float sensitivity) {
|
void FilamentViewer::setCameraExposure(float aperture, float shutterSpeed, float sensitivity)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
Log("Setting aperture (%03f) shutterSpeed (%03f) and sensitivity (%03f)", aperture, shutterSpeed, sensitivity);
|
Log("Setting aperture (%03f) shutterSpeed (%03f) and sensitivity (%03f)", aperture, shutterSpeed, sensitivity);
|
||||||
cam.setExposure(aperture, shutterSpeed, sensitivity);
|
cam.setExposure(aperture, shutterSpeed, sensitivity);
|
||||||
@@ -686,7 +740,8 @@ void FilamentViewer::setCameraExposure(float aperture, float shutterSpeed, float
|
|||||||
///
|
///
|
||||||
/// Set the focal length of the active camera.
|
/// Set the focal length of the active camera.
|
||||||
///
|
///
|
||||||
void FilamentViewer::setCameraFocalLength(float focalLength) {
|
void FilamentViewer::setCameraFocalLength(float focalLength)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
_cameraFocalLength = focalLength;
|
_cameraFocalLength = focalLength;
|
||||||
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
|
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
|
||||||
@@ -696,7 +751,8 @@ void FilamentViewer::setCameraFocalLength(float focalLength) {
|
|||||||
///
|
///
|
||||||
/// Set the focus distance of the active camera.
|
/// Set the focus distance of the active camera.
|
||||||
///
|
///
|
||||||
void FilamentViewer::setCameraFocusDistance(float focusDistance) {
|
void FilamentViewer::setCameraFocusDistance(float focusDistance)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
_cameraFocusDistance = focusDistance;
|
_cameraFocusDistance = focusDistance;
|
||||||
cam.setFocusDistance(_cameraFocusDistance);
|
cam.setFocusDistance(_cameraFocusDistance);
|
||||||
@@ -707,15 +763,18 @@ void FilamentViewer::setCameraFocusDistance(float focusDistance) {
|
|||||||
/// N.B. Blender will generally export a three-node hierarchy -
|
/// N.B. Blender will generally export a three-node hierarchy -
|
||||||
/// Camera1->Camera_Orientation->Camera2. The correct name will be the Camera_Orientation.
|
/// Camera1->Camera_Orientation->Camera2. The correct name will be the Camera_Orientation.
|
||||||
///
|
///
|
||||||
bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName) {
|
bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName)
|
||||||
|
{
|
||||||
|
|
||||||
auto asset = _assetManager->getAssetByEntityId(entityId);
|
auto asset = _assetManager->getAssetByEntityId(entityId);
|
||||||
if(!asset) {
|
if (!asset)
|
||||||
|
{
|
||||||
Log("Failed to find asset under entity id %d.", entityId);
|
Log("Failed to find asset under entity id %d.", entityId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
size_t count = asset->getCameraEntityCount();
|
size_t count = asset->getCameraEntityCount();
|
||||||
if (count == 0) {
|
if (count == 0)
|
||||||
|
{
|
||||||
Log("No cameras found attached to specified entity.");
|
Log("No cameras found attached to specified entity.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -724,28 +783,35 @@ bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName) {
|
|||||||
|
|
||||||
utils::Entity target;
|
utils::Entity target;
|
||||||
|
|
||||||
if(!cameraName) {
|
if (!cameraName)
|
||||||
|
{
|
||||||
auto inst = _ncm->getInstance(cameras[0]);
|
auto inst = _ncm->getInstance(cameras[0]);
|
||||||
const char *name = _ncm->getName(inst);
|
const char *name = _ncm->getName(inst);
|
||||||
target = cameras[0];
|
target = cameras[0];
|
||||||
Log("No camera specified, using first camera node found (%s)", name);
|
Log("No camera specified, using first camera node found (%s)", name);
|
||||||
} else {
|
}
|
||||||
for (int j = 0; j < count; j++) {
|
else
|
||||||
|
{
|
||||||
|
for (int j = 0; j < count; j++)
|
||||||
|
{
|
||||||
auto inst = _ncm->getInstance(cameras[j]);
|
auto inst = _ncm->getInstance(cameras[j]);
|
||||||
const char *name = _ncm->getName(inst);
|
const char *name = _ncm->getName(inst);
|
||||||
if (strcmp(name, cameraName) == 0) {
|
if (strcmp(name, cameraName) == 0)
|
||||||
|
{
|
||||||
target = cameras[j];
|
target = cameras[j];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(target.isNull()) {
|
if (target.isNull())
|
||||||
|
{
|
||||||
Log("Unable to locate camera under name %s ", cameraName);
|
Log("Unable to locate camera under name %s ", cameraName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera *camera = _engine->getCameraComponent(target);
|
Camera *camera = _engine->getCameraComponent(target);
|
||||||
if(!camera) {
|
if (!camera)
|
||||||
|
{
|
||||||
Log("Failed to retrieve camera component for target");
|
Log("Failed to retrieve camera component for target");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -761,11 +827,13 @@ bool FilamentViewer::setCamera(EntityId entityId, const char *cameraName) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadSkybox(const char *const skyboxPath) {
|
void FilamentViewer::loadSkybox(const char *const skyboxPath)
|
||||||
|
{
|
||||||
|
|
||||||
removeSkybox();
|
removeSkybox();
|
||||||
|
|
||||||
if (!skyboxPath) {
|
if (!skyboxPath)
|
||||||
|
{
|
||||||
Log("No skybox path provided, removed skybox.");
|
Log("No skybox path provided, removed skybox.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,7 +844,8 @@ void FilamentViewer::loadSkybox(const char *const skyboxPath) {
|
|||||||
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
||||||
ResourceBuffer *skyboxBufferCopy = new ResourceBuffer(skyboxBuffer);
|
ResourceBuffer *skyboxBufferCopy = new ResourceBuffer(skyboxBuffer);
|
||||||
|
|
||||||
if(skyboxBuffer.size <= 0) {
|
if (skyboxBuffer.size <= 0)
|
||||||
|
{
|
||||||
Log("Could not load skybox resource.");
|
Log("Could not load skybox resource.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -790,37 +859,43 @@ void FilamentViewer::loadSkybox(const char *const skyboxPath) {
|
|||||||
static_cast<uint32_t>(skyboxBuffer.size));
|
static_cast<uint32_t>(skyboxBuffer.size));
|
||||||
|
|
||||||
_skyboxTexture =
|
_skyboxTexture =
|
||||||
ktxreader::Ktx1Reader::createTexture(_engine, *skyboxBundle, false, [](void* userdata) {
|
ktxreader::Ktx1Reader::createTexture(
|
||||||
|
_engine, *skyboxBundle, false, [](void *userdata)
|
||||||
|
{
|
||||||
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
||||||
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
||||||
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
||||||
loader->free(*rb);
|
loader->free(*rb);
|
||||||
delete rb;
|
delete rb;
|
||||||
delete vec;
|
delete vec;
|
||||||
Log("Skybox load complete.");
|
Log("Skybox load complete."); },
|
||||||
}, callbackData);
|
callbackData);
|
||||||
_skybox =
|
_skybox =
|
||||||
filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine);
|
filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine);
|
||||||
|
|
||||||
_scene->setSkybox(_skybox);
|
_scene->setSkybox(_skybox);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeSkybox() {
|
void FilamentViewer::removeSkybox()
|
||||||
|
{
|
||||||
Log("Removing skybox");
|
Log("Removing skybox");
|
||||||
_scene->setSkybox(nullptr);
|
_scene->setSkybox(nullptr);
|
||||||
if(_skybox) {
|
if (_skybox)
|
||||||
|
{
|
||||||
_engine->destroy(_skybox);
|
_engine->destroy(_skybox);
|
||||||
_skybox = nullptr;
|
_skybox = nullptr;
|
||||||
}
|
}
|
||||||
if(_skyboxTexture) {
|
if (_skyboxTexture)
|
||||||
|
{
|
||||||
_engine->destroy(_skyboxTexture);
|
_engine->destroy(_skyboxTexture);
|
||||||
_skyboxTexture = nullptr;
|
_skyboxTexture = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::removeIbl() {
|
void FilamentViewer::removeIbl()
|
||||||
if(_indirectLight) {
|
{
|
||||||
|
if (_indirectLight)
|
||||||
|
{
|
||||||
_engine->destroy(_indirectLight);
|
_engine->destroy(_indirectLight);
|
||||||
_engine->destroy(_iblTexture);
|
_engine->destroy(_iblTexture);
|
||||||
_indirectLight = nullptr;
|
_indirectLight = nullptr;
|
||||||
@@ -829,9 +904,11 @@ void FilamentViewer::removeIbl() {
|
|||||||
_scene->setIndirectLight(nullptr);
|
_scene->setIndirectLight(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::loadIbl(const char *const iblPath, float intensity) {
|
void FilamentViewer::loadIbl(const char *const iblPath, float intensity)
|
||||||
|
{
|
||||||
removeIbl();
|
removeIbl();
|
||||||
if (iblPath) {
|
if (iblPath)
|
||||||
|
{
|
||||||
Log("Loading IBL from %s", iblPath);
|
Log("Loading IBL from %s", iblPath);
|
||||||
|
|
||||||
// Load IBL.
|
// Load IBL.
|
||||||
@@ -839,7 +916,8 @@ void FilamentViewer::loadIbl(const char *const iblPath, float intensity) {
|
|||||||
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
// because this will go out of scope before the texture callback is invoked, we need to make a copy to the heap
|
||||||
ResourceBuffer *iblBufferCopy = new ResourceBuffer(iblBuffer);
|
ResourceBuffer *iblBufferCopy = new ResourceBuffer(iblBuffer);
|
||||||
|
|
||||||
if(iblBuffer.size == 0) {
|
if (iblBuffer.size == 0)
|
||||||
|
{
|
||||||
Log("Error loading IBL, resource could not be loaded.");
|
Log("Error loading IBL, resource could not be loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -853,14 +931,16 @@ void FilamentViewer::loadIbl(const char *const iblPath, float intensity) {
|
|||||||
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, iblBufferCopy};
|
std::vector<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, iblBufferCopy};
|
||||||
|
|
||||||
_iblTexture =
|
_iblTexture =
|
||||||
ktxreader::Ktx1Reader::createTexture(_engine, *iblBundle, false, [](void* userdata) {
|
ktxreader::Ktx1Reader::createTexture(
|
||||||
|
_engine, *iblBundle, false, [](void *userdata)
|
||||||
|
{
|
||||||
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
std::vector<void*>* vec = (std::vector<void*>*)userdata;
|
||||||
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
ResourceLoaderWrapper* loader = (ResourceLoaderWrapper*)vec->at(0);
|
||||||
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
ResourceBuffer* rb = (ResourceBuffer*) vec->at(1);
|
||||||
loader->free(*rb);
|
loader->free(*rb);
|
||||||
delete rb;
|
delete rb;
|
||||||
delete vec;
|
delete vec; },
|
||||||
}, callbackData);
|
callbackData);
|
||||||
_indirectLight = IndirectLight::Builder()
|
_indirectLight = IndirectLight::Builder()
|
||||||
.reflections(_iblTexture)
|
.reflections(_iblTexture)
|
||||||
.irradiance(3, harmonics)
|
.irradiance(3, harmonics)
|
||||||
@@ -879,14 +959,17 @@ void FilamentViewer::render(
|
|||||||
uint64_t frameTimeInNanos,
|
uint64_t frameTimeInNanos,
|
||||||
void *pixelBuffer,
|
void *pixelBuffer,
|
||||||
void (*callback)(void *buf, size_t size, void *data),
|
void (*callback)(void *buf, size_t size, void *data),
|
||||||
void* data) {
|
void *data)
|
||||||
|
{
|
||||||
|
|
||||||
if (!_view || !_mainCamera || !_swapChain) {
|
if (!_view || !_mainCamera || !_swapChain)
|
||||||
|
{
|
||||||
Log("Not ready for rendering");
|
Log("Not ready for rendering");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_frameCount == 60) {
|
if (_frameCount == 60)
|
||||||
|
{
|
||||||
// Log("1 sec average for asset animation update %f", _elapsed / 60);
|
// Log("1 sec average for asset animation update %f", _elapsed / 60);
|
||||||
_elapsed = 0;
|
_elapsed = 0;
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
@@ -899,7 +982,18 @@ void FilamentViewer::render(
|
|||||||
_elapsed += tmr.elapsed();
|
_elapsed += tmr.elapsed();
|
||||||
_frameCount++;
|
_frameCount++;
|
||||||
|
|
||||||
if(pixelBuffer) {
|
// if a manipulator is active, update the active camera orientation
|
||||||
|
if(_manipulator) {
|
||||||
|
math::double3 eye, target, upward;
|
||||||
|
Camera& cam =_view->getCamera();
|
||||||
|
_manipulator->getLookAt(&eye, &target, &upward);
|
||||||
|
cam.lookAt(eye, target, upward);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - this was an experiment but probably useful to keep for debugging
|
||||||
|
// if pixelBuffer is provided, we will copy the framebuffer into the pixelBuffer.
|
||||||
|
if (pixelBuffer)
|
||||||
|
{
|
||||||
auto pbd = Texture::PixelBufferDescriptor(
|
auto pbd = Texture::PixelBufferDescriptor(
|
||||||
pixelBuffer, size_t(1024 * 768 * 4),
|
pixelBuffer, size_t(1024 * 768 * 4),
|
||||||
Texture::Format::RGBA,
|
Texture::Format::RGBA,
|
||||||
@@ -909,21 +1003,27 @@ void FilamentViewer::render(
|
|||||||
_renderer->render(_view);
|
_renderer->render(_view);
|
||||||
_renderer->readPixels(0, 0, 1024, 768, std::move(pbd));
|
_renderer->readPixels(0, 0, 1024, 768, std::move(pbd));
|
||||||
_renderer->endFrame();
|
_renderer->endFrame();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Render the scene, unless the renderer wants to skip the frame.
|
// Render the scene, unless the renderer wants to skip the frame.
|
||||||
if (_renderer->beginFrame(_swapChain, frameTimeInNanos)) {
|
if (_renderer->beginFrame(_swapChain, frameTimeInNanos))
|
||||||
|
{
|
||||||
_renderer->render(_view);
|
_renderer->render(_view);
|
||||||
_renderer->endFrame();
|
_renderer->endFrame();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// skipped frame
|
// skipped frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::updateViewportAndCameraProjection(
|
void FilamentViewer::updateViewportAndCameraProjection(
|
||||||
int width, int height, float contentScaleFactor) {
|
int width, int height, float contentScaleFactor)
|
||||||
if (!_view || !_mainCamera) {
|
{
|
||||||
|
if (!_view || !_mainCamera)
|
||||||
|
{
|
||||||
Log("Skipping camera update, no view or camrea");
|
Log("Skipping camera update, no view or camrea");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -944,20 +1044,24 @@ void FilamentViewer::updateViewportAndCameraProjection(
|
|||||||
contentScaleFactor);
|
contentScaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setViewFrustumCulling(bool enabled) {
|
void FilamentViewer::setViewFrustumCulling(bool enabled)
|
||||||
|
{
|
||||||
_view->setFrustumCullingEnabled(enabled);
|
_view->setFrustumCullingEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setCameraPosition(float x, float y, float z) {
|
void FilamentViewer::setCameraPosition(float x, float y, float z)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
|
|
||||||
_cameraPosition = math::mat4f::translation(math::float3(x, y, z));
|
_cameraPosition = math::mat4f::translation(math::float3(x, y, z));
|
||||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
void FilamentViewer::moveCameraToAsset(EntityId entityId)
|
||||||
|
{
|
||||||
auto asset = _assetManager->getAssetByEntityId(entityId);
|
auto asset = _assetManager->getAssetByEntityId(entityId);
|
||||||
if(!asset) {
|
if (!asset)
|
||||||
|
{
|
||||||
Log("Failed to find asset attached to specified entity id.");
|
Log("Failed to find asset attached to specified entity id.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -971,13 +1075,15 @@ void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
|||||||
Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar());
|
Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setCameraRotation(float rads, float x, float y, float z) {
|
void FilamentViewer::setCameraRotation(float rads, float x, float y, float z)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
_cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z));
|
_cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z));
|
||||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setCameraModelMatrix(const float* const matrix) {
|
void FilamentViewer::setCameraModelMatrix(const float *const matrix)
|
||||||
|
{
|
||||||
Camera &cam = _view->getCamera();
|
Camera &cam = _view->getCamera();
|
||||||
|
|
||||||
mat4 modelMatrix(
|
mat4 modelMatrix(
|
||||||
@@ -996,75 +1102,123 @@ void FilamentViewer::setCameraModelMatrix(const float* const matrix) {
|
|||||||
matrix[12],
|
matrix[12],
|
||||||
matrix[13],
|
matrix[13],
|
||||||
matrix[14],
|
matrix[14],
|
||||||
matrix[15]
|
matrix[15]);
|
||||||
);
|
|
||||||
cam.setModelMatrix(modelMatrix);
|
cam.setModelMatrix(modelMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::grabBegin(float x, float y, bool pan) {
|
void FilamentViewer::_createManipulator()
|
||||||
if (!_view || !_mainCamera || !_swapChain) {
|
{
|
||||||
Log("View not ready, ignoring grab");
|
Camera &cam = _view->getCamera();
|
||||||
return;
|
math::double3 home = cam.getPosition();
|
||||||
}
|
math::double3 up = cam.getUpVector();
|
||||||
_panning = pan;
|
math::double3 target = home + cam.getForwardVector();
|
||||||
_startX = x;
|
Viewport const &vp = _view->getViewport();
|
||||||
_startY = y;
|
Log("Creating manipulator for viewport size %dx%d with camera norm %f", vp.width, vp.height, norm(home));
|
||||||
|
float zoomSpeed = norm(home) / 10;
|
||||||
|
zoomSpeed = math::clamp(zoomSpeed, 0.1f, 100000.0f);
|
||||||
|
|
||||||
|
_manipulator = Manipulator<double>::Builder()
|
||||||
|
.viewport(vp.width, vp.height)
|
||||||
|
.orbitHomePosition(home[0], home[1], home[2])
|
||||||
|
.upVector(up.x, up.y, up.z)
|
||||||
|
.zoomSpeed(zoomSpeed)
|
||||||
|
// .orbitSpeed(0.0001, 0.0001)
|
||||||
|
.targetPosition(target[0], target[1], target[2])
|
||||||
|
.build(Mode::ORBIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::grabUpdate(float x, float y) {
|
void FilamentViewer::grabBegin(float x, float y, bool pan)
|
||||||
if (!_view || !_swapChain) {
|
{
|
||||||
|
if (!_view || !_mainCamera || !_swapChain)
|
||||||
|
{
|
||||||
Log("View not ready, ignoring grab");
|
Log("View not ready, ignoring grab");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Camera& cam =_view->getCamera();
|
if (!_manipulator)
|
||||||
auto eye = cam.getPosition();
|
{
|
||||||
auto target = eye + cam.getForwardVector();
|
_createManipulator();
|
||||||
auto upward = cam.getUpVector();
|
}
|
||||||
Viewport const& vp = _view->getViewport();
|
if(pan) {
|
||||||
if(_panning) {
|
Log("Beginning pan at %f %f", x, y);
|
||||||
auto trans = cam.getModelMatrix() * mat4::translation(math::float3 { 50 * (x - _startX) / vp.width, 50 * (y - _startY) / vp.height, 0.0f });
|
|
||||||
cam.setModelMatrix(trans);
|
|
||||||
} else {
|
} else {
|
||||||
auto trans = cam.getModelMatrix() * mat4::rotation(0.05,
|
Log("Beginning rotate at %f %f", x, y);
|
||||||
math::float3 { (y - _startY) / vp.height, (x - _startX) / vp.width, 0.0f });
|
|
||||||
cam.setModelMatrix(trans);
|
|
||||||
}
|
|
||||||
_startX = x;
|
|
||||||
_startY = y;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::grabEnd() {
|
_manipulator->grabBegin(x, y, pan);
|
||||||
if (!_view || !_mainCamera || !_swapChain) {
|
}
|
||||||
|
|
||||||
|
void FilamentViewer::grabUpdate(float x, float y)
|
||||||
|
{
|
||||||
|
if (!_view || !_swapChain)
|
||||||
|
{
|
||||||
Log("View not ready, ignoring grab");
|
Log("View not ready, ignoring grab");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log("Updating grab at %f %f", x, y);
|
||||||
|
|
||||||
|
|
||||||
|
if (_manipulator)
|
||||||
|
{
|
||||||
|
_manipulator->grabUpdate(x, y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Error - trying to use a manipulator when one is not available. Ensure you call grabBegin before grabUpdate/grabEnd");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::scrollBegin() {
|
void FilamentViewer::grabEnd()
|
||||||
// noop
|
{
|
||||||
|
if (!_view || !_mainCamera || !_swapChain)
|
||||||
|
{
|
||||||
|
Log("View not ready, ignoring grab");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_manipulator)
|
||||||
|
{
|
||||||
|
_manipulator->grabEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Error - trying to use a manipulator when one is not available. Ensure you call grabBegin before grabUpdate/grabEnd");
|
||||||
|
}
|
||||||
|
delete _manipulator;
|
||||||
|
_manipulator = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::scrollUpdate(float x, float y, float delta) {
|
void FilamentViewer::scrollBegin()
|
||||||
Camera& cam =_view->getCamera();
|
{
|
||||||
Viewport const& vp = _view->getViewport();
|
if (!_manipulator)
|
||||||
auto trans = cam.getModelMatrix() * mat4::translation(math::float3 {0.0f, 0.0f, delta });
|
{
|
||||||
cam.setModelMatrix(trans);
|
_createManipulator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::scrollEnd() {
|
void FilamentViewer::scrollUpdate(float x, float y, float delta)
|
||||||
// noop
|
{
|
||||||
|
if (_manipulator)
|
||||||
|
{
|
||||||
|
_manipulator->scroll(int(x), int(y), delta);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Error - trying to use a manipulator when one is not available. Ensure you call grabBegin before grabUpdate/grabEnd");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId* entityId) {
|
void FilamentViewer::scrollEnd()
|
||||||
Log("Picking at %d,%d", x, y);
|
{
|
||||||
|
delete _manipulator;
|
||||||
|
_manipulator = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId)
|
||||||
|
{
|
||||||
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result) {
|
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result) {
|
||||||
|
|
||||||
*entityId = Entity::smuggle(result.renderable);
|
*entityId = Entity::smuggle(result.renderable);
|
||||||
Log("Got result %d", *entityId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace polyvox
|
} // namespace polyvox
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
98
macos/src/camutils/Bookmark.cpp
Normal file
98
macos/src/camutils/Bookmark.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <camutils/Bookmark.h>
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
#include <math/vec3.h>
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
Bookmark<FLOAT> Bookmark<FLOAT>::interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t) {
|
||||||
|
Bookmark<FLOAT> result;
|
||||||
|
using float3 = filament::math::vec3<FLOAT>;
|
||||||
|
|
||||||
|
if (a.mode == Mode::MAP) {
|
||||||
|
assert(b.mode == Mode::MAP);
|
||||||
|
const double rho = sqrt(2.0);
|
||||||
|
const double rho2 = 2, rho4 = 4;
|
||||||
|
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
|
||||||
|
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
|
||||||
|
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
|
||||||
|
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
|
||||||
|
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
|
||||||
|
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
|
||||||
|
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
|
||||||
|
const double dr = r1 - r0;
|
||||||
|
const int valid = !std::isnan(dr) && dr != 0;
|
||||||
|
const double S = (valid ? dr : log(w1 / w0)) / rho;
|
||||||
|
const double s = t * S;
|
||||||
|
|
||||||
|
// This performs Van Wijk interpolation to animate between two waypoints on a map.
|
||||||
|
if (valid) {
|
||||||
|
const double coshr0 = cosh(r0);
|
||||||
|
const double u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
|
||||||
|
Bookmark<FLOAT> result;
|
||||||
|
result.map.center.x = ux0 + u * dx;
|
||||||
|
result.map.center.y = uy0 + u * dy;
|
||||||
|
result.map.extent = w0 * coshr0 / cosh(rho * s + r0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For degenerate cases, fall back to a simplified interpolation method.
|
||||||
|
result.map.center.x = ux0 + t * dx;
|
||||||
|
result.map.center.y = uy0 + t * dy;
|
||||||
|
result.map.extent = w0 * exp(rho * s);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(b.mode == Mode::ORBIT);
|
||||||
|
result.orbit.phi = lerp(a.orbit.phi, b.orbit.phi, FLOAT(t));
|
||||||
|
result.orbit.theta = lerp(a.orbit.theta, b.orbit.theta, FLOAT(t));
|
||||||
|
result.orbit.distance = lerp(a.orbit.distance, b.orbit.distance, FLOAT(t));
|
||||||
|
result.orbit.pivot = lerp(a.orbit.pivot, b.orbit.pivot, float3(t));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses the Van Wijk method to suggest a duration for animating between two waypoints on a map.
|
||||||
|
// This does not have units, so just use it as a multiplier.
|
||||||
|
template <typename FLOAT>
|
||||||
|
double Bookmark<FLOAT>::duration(Bookmark<FLOAT> a, Bookmark<FLOAT> b) {
|
||||||
|
assert(a.mode == Mode::ORBIT && b.mode == Mode::ORBIT);
|
||||||
|
const double rho = sqrt(2.0);
|
||||||
|
const double rho2 = 2, rho4 = 4;
|
||||||
|
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
|
||||||
|
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
|
||||||
|
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
|
||||||
|
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
|
||||||
|
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
|
||||||
|
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
|
||||||
|
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
|
||||||
|
const double dr = r1 - r0;
|
||||||
|
const int valid = !std::isnan(dr) && dr != 0;
|
||||||
|
const double S = (valid ? dr : log(w1 / w0)) / rho;
|
||||||
|
return fabs(S);
|
||||||
|
}
|
||||||
|
|
||||||
|
template class Bookmark<float>;
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
206
macos/src/camutils/FreeFlightManipulator.h
Normal file
206
macos/src/camutils/FreeFlightManipulator.h
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAMUTILS_FREEFLIGHT_MANIPULATOR_H
|
||||||
|
#define CAMUTILS_FREEFLIGHT_MANIPULATOR_H
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
#include <math/mat3.h>
|
||||||
|
#include <math/mat4.h>
|
||||||
|
#include <math/quat.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
class FreeFlightManipulator : public Manipulator<FLOAT> {
|
||||||
|
public:
|
||||||
|
using vec2 = filament::math::vec2<FLOAT>;
|
||||||
|
using vec3 = filament::math::vec3<FLOAT>;
|
||||||
|
using vec4 = filament::math::vec4<FLOAT>;
|
||||||
|
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||||
|
using Base = Manipulator<FLOAT>;
|
||||||
|
using Config = typename Base::Config;
|
||||||
|
|
||||||
|
FreeFlightManipulator(Mode mode, const Config& props) : Base(mode, props) {
|
||||||
|
setProperties(props);
|
||||||
|
Base::mEye = Base::mProps.flightStartPosition;
|
||||||
|
const auto pitch = Base::mProps.flightStartPitch;
|
||||||
|
const auto yaw = Base::mProps.flightStartYaw;
|
||||||
|
mTargetEuler = {pitch, yaw};
|
||||||
|
updateTarget(pitch, yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProperties(const Config& props) override {
|
||||||
|
Config resolved = props;
|
||||||
|
|
||||||
|
if (resolved.flightPanSpeed == vec2(0, 0)) {
|
||||||
|
resolved.flightPanSpeed = vec2(0.01, 0.01);
|
||||||
|
}
|
||||||
|
if (resolved.flightMaxSpeed == 0.0) {
|
||||||
|
resolved.flightMaxSpeed = 10.0;
|
||||||
|
}
|
||||||
|
if (resolved.flightSpeedSteps == 0) {
|
||||||
|
resolved.flightSpeedSteps = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
Base::setProperties(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTarget(FLOAT pitch, FLOAT yaw) {
|
||||||
|
Base::mTarget = Base::mEye + (mat3::eulerZYX(0, yaw, pitch) * vec3(0.0, 0.0, -1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabBegin(int x, int y, bool strafe) override {
|
||||||
|
mGrabWin = {x, y};
|
||||||
|
mGrabbing = true;
|
||||||
|
mGrabEuler = mTargetEuler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabUpdate(int x, int y) override {
|
||||||
|
if (!mGrabbing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vec2 del = mGrabWin - vec2{x, y};
|
||||||
|
|
||||||
|
const auto& grabPitch = mGrabEuler.x;
|
||||||
|
const auto& grabYaw = mGrabEuler.y;
|
||||||
|
auto& pitch = mTargetEuler.x;
|
||||||
|
auto& yaw = mTargetEuler.y;
|
||||||
|
|
||||||
|
constexpr double EPSILON = 0.001;
|
||||||
|
|
||||||
|
auto panSpeed = Base::mProps.flightPanSpeed;
|
||||||
|
constexpr FLOAT minPitch = (-F_PI_2 + EPSILON);
|
||||||
|
constexpr FLOAT maxPitch = ( F_PI_2 - EPSILON);
|
||||||
|
pitch = clamp(grabPitch + del.y * -panSpeed.y, minPitch, maxPitch);
|
||||||
|
yaw = fmod(grabYaw + del.x * panSpeed.x, 2.0 * F_PI);
|
||||||
|
|
||||||
|
updateTarget(pitch, yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabEnd() override {
|
||||||
|
mGrabbing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyDown(typename Base::Key key) override {
|
||||||
|
mKeyDown[(int) key] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyUp(typename Base::Key key) override {
|
||||||
|
mKeyDown[(int) key] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(int x, int y, FLOAT scrolldelta) override {
|
||||||
|
const FLOAT halfSpeedSteps = Base::mProps.flightSpeedSteps / 2;
|
||||||
|
mScrollWheel = clamp(mScrollWheel + scrolldelta, -halfSpeedSteps, halfSpeedSteps);
|
||||||
|
// Normalize the scroll position from -1 to 1 and calculate the move speed, in world
|
||||||
|
// units per second.
|
||||||
|
mScrollPositionNormalized = (mScrollWheel + halfSpeedSteps) / halfSpeedSteps - 1.0;
|
||||||
|
mMoveSpeed = pow(Base::mProps.flightMaxSpeed, mScrollPositionNormalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(FLOAT deltaTime) override {
|
||||||
|
vec3 forceLocal { 0.0, 0.0, 0.0 };
|
||||||
|
|
||||||
|
if (mKeyDown[(int) Base::Key::FORWARD]) {
|
||||||
|
forceLocal += vec3{ 0.0, 0.0, -1.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::LEFT]) {
|
||||||
|
forceLocal += vec3{ -1.0, 0.0, 0.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::BACKWARD]) {
|
||||||
|
forceLocal += vec3{ 0.0, 0.0, 1.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::RIGHT]) {
|
||||||
|
forceLocal += vec3{ 1.0, 0.0, 0.0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
|
||||||
|
vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz;
|
||||||
|
|
||||||
|
if (mKeyDown[(int) Base::Key::UP]) {
|
||||||
|
forceWorld += vec3{ 0.0, 1.0, 0.0 };
|
||||||
|
}
|
||||||
|
if (mKeyDown[(int) Base::Key::DOWN]) {
|
||||||
|
forceWorld += vec3{ 0.0, -1.0, 0.0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
forceWorld *= mMoveSpeed;
|
||||||
|
|
||||||
|
const auto dampingFactor = Base::mProps.flightMoveDamping;
|
||||||
|
if (dampingFactor == 0.0) {
|
||||||
|
// Without damping, we simply treat the force as our velocity.
|
||||||
|
mEyeVelocity = forceWorld;
|
||||||
|
} else {
|
||||||
|
// The dampingFactor acts as "friction", which acts upon the camera in the direction
|
||||||
|
// opposite its velocity.
|
||||||
|
// Force is also multiplied by the dampingFactor, to "make up" for the friction.
|
||||||
|
// This ensures that the max velocity still approaches mMoveSpeed;
|
||||||
|
vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor;
|
||||||
|
mEyeVelocity += velocityDelta * deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vec3 positionDelta = mEyeVelocity * deltaTime;
|
||||||
|
|
||||||
|
Base::mEye += positionDelta;
|
||||||
|
Base::mTarget += positionDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getCurrentBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.flight.position = Base::mEye;
|
||||||
|
bookmark.flight.pitch = mTargetEuler.x;
|
||||||
|
bookmark.flight.yaw = mTargetEuler.y;
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getHomeBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.flight.position = Base::mProps.flightStartPosition;
|
||||||
|
bookmark.flight.pitch = Base::mProps.flightStartPitch;
|
||||||
|
bookmark.flight.yaw = Base::mProps.flightStartYaw;
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpToBookmark(const Bookmark& bookmark) override {
|
||||||
|
Base::mEye = bookmark.flight.position;
|
||||||
|
updateTarget(bookmark.flight.pitch, bookmark.flight.yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
vec2 mGrabWin;
|
||||||
|
vec2 mTargetEuler; // (pitch, yaw)
|
||||||
|
vec2 mGrabEuler; // (pitch, yaw)
|
||||||
|
bool mKeyDown[(int) Base::Key::COUNT] = {false};
|
||||||
|
bool mGrabbing = false;
|
||||||
|
FLOAT mScrollWheel = 0.0f;
|
||||||
|
FLOAT mScrollPositionNormalized = 0.0f;
|
||||||
|
FLOAT mMoveSpeed = 1.0f;
|
||||||
|
vec3 mEyeVelocity;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
|
|
||||||
|
#endif /* CAMUTILS_FREEFLIGHT_MANIPULATOR_H */
|
||||||
324
macos/src/camutils/Manipulator.cpp
Normal file
324
macos/src/camutils/Manipulator.cpp
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
|
||||||
|
#include "FreeFlightManipulator.h"
|
||||||
|
#include "MapManipulator.h"
|
||||||
|
#include "OrbitManipulator.h"
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::viewport(int width, int height) {
|
||||||
|
details.viewport[0] = width;
|
||||||
|
details.viewport[1] = height;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.targetPosition = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::upVector(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.upVector = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::zoomSpeed(FLOAT val) {
|
||||||
|
details.zoomSpeed = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.orbitHomePosition = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitSpeed(FLOAT x, FLOAT y) {
|
||||||
|
details.orbitSpeed = {x, y};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDirection(Fov fov) {
|
||||||
|
details.fovDirection = fov;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDegrees(FLOAT degrees) {
|
||||||
|
details.fovDegrees = degrees;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::farPlane(FLOAT distance) {
|
||||||
|
details.farPlane = distance;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight) {
|
||||||
|
details.mapExtent = {worldWidth, worldHeight};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapMinDistance(FLOAT mindist) {
|
||||||
|
details.mapMinDistance = mindist;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartPosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||||
|
details.flightStartPosition = {x, y, z};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw) {
|
||||||
|
details.flightStartPitch = pitch;
|
||||||
|
details.flightStartYaw = yaw;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMaxMoveSpeed(FLOAT maxSpeed) {
|
||||||
|
details.flightMaxSpeed = maxSpeed;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightSpeedSteps(int steps) {
|
||||||
|
details.flightSpeedSteps = steps;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightPanSpeed(FLOAT x, FLOAT y) {
|
||||||
|
details.flightPanSpeed = {x, y};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMoveDamping(FLOAT damping) {
|
||||||
|
details.flightMoveDamping = damping;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d) {
|
||||||
|
details.groundPlane = {a, b, c, d};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT> typename
|
||||||
|
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::raycastCallback(RayCallback cb, void* userdata) {
|
||||||
|
details.raycastCallback = cb;
|
||||||
|
details.raycastUserdata = userdata;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
Manipulator<FLOAT>* Manipulator<FLOAT>::Builder::build(Mode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case Mode::FREE_FLIGHT:
|
||||||
|
return new FreeFlightManipulator<FLOAT>(mode, details);
|
||||||
|
case Mode::MAP:
|
||||||
|
return new MapManipulator<FLOAT>(mode, details);
|
||||||
|
case Mode::ORBIT:
|
||||||
|
return new OrbitManipulator<FLOAT>(mode, details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
Manipulator<FLOAT>::Manipulator(Mode mode, const Config& props) : mMode(mode) {
|
||||||
|
setProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::setProperties(const Config& props) {
|
||||||
|
mProps = props;
|
||||||
|
|
||||||
|
if (mProps.zoomSpeed == FLOAT(0)) {
|
||||||
|
mProps.zoomSpeed = 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.upVector == vec3(0)) {
|
||||||
|
mProps.upVector = vec3(0, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.fovDegrees == FLOAT(0)) {
|
||||||
|
mProps.fovDegrees = 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.farPlane == FLOAT(0)) {
|
||||||
|
mProps.farPlane = 5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProps.mapExtent == vec2(0)) {
|
||||||
|
mProps.mapExtent = vec2(512);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::setViewport(int width, int height) {
|
||||||
|
Config props = mProps;
|
||||||
|
props.viewport[0] = width;
|
||||||
|
props.viewport[1] = height;
|
||||||
|
setProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const {
|
||||||
|
*targetPosition = mTarget;
|
||||||
|
*eyePosition = mEye;
|
||||||
|
const vec3 gaze = normalize(mTarget - mEye);
|
||||||
|
const vec3 right = cross(gaze, mProps.upVector);
|
||||||
|
*upward = cross(right, gaze);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
static bool raycastPlane(const filament::math::vec3<FLOAT>& origin,
|
||||||
|
const filament::math::vec3<FLOAT>& dir, FLOAT* t, void* userdata) {
|
||||||
|
using vec3 = filament::math::vec3<FLOAT>;
|
||||||
|
using vec4 = filament::math::vec4<FLOAT>;
|
||||||
|
auto props = (const typename Manipulator<FLOAT>::Config*) userdata;
|
||||||
|
const vec4 plane = props->groundPlane;
|
||||||
|
const vec3 n = vec3(plane[0], plane[1], plane[2]);
|
||||||
|
const vec3 p0 = n * plane[3];
|
||||||
|
const FLOAT denom = -dot(n, dir);
|
||||||
|
if (denom > 1e-6) {
|
||||||
|
const vec3 p0l0 = p0 - origin;
|
||||||
|
*t = dot(p0l0, n) / -denom;
|
||||||
|
return *t >= 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::getRay(int x, int y, vec3* porigin, vec3* pdir) const {
|
||||||
|
const vec3 gaze = normalize(mTarget - mEye);
|
||||||
|
const vec3 right = normalize(cross(gaze, mProps.upVector));
|
||||||
|
const vec3 upward = cross(right, gaze);
|
||||||
|
const FLOAT width = mProps.viewport[0];
|
||||||
|
const FLOAT height = mProps.viewport[1];
|
||||||
|
const FLOAT fov = mProps.fovDegrees * F_PI / 180.0;
|
||||||
|
|
||||||
|
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
|
||||||
|
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
|
||||||
|
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
|
||||||
|
|
||||||
|
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
|
||||||
|
const FLOAT tangent = tan(fov / 2.0);
|
||||||
|
const FLOAT aspect = width / height;
|
||||||
|
|
||||||
|
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
|
||||||
|
vec3 dir = gaze;
|
||||||
|
if (mProps.fovDirection == Fov::VERTICAL) {
|
||||||
|
dir += right * tangent * u * aspect;
|
||||||
|
dir += upward * tangent * v;
|
||||||
|
} else {
|
||||||
|
dir += right * tangent * u;
|
||||||
|
dir += upward * tangent * v / aspect;
|
||||||
|
}
|
||||||
|
dir = normalize(dir);
|
||||||
|
|
||||||
|
*porigin = mEye;
|
||||||
|
*pdir = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
bool Manipulator<FLOAT>::raycast(int x, int y, vec3* result) const {
|
||||||
|
vec3 origin, dir;
|
||||||
|
getRay(x, y, &origin, &dir);
|
||||||
|
|
||||||
|
// Choose either the user's callback function or the plane intersector.
|
||||||
|
auto callback = mProps.raycastCallback;
|
||||||
|
auto fallback = raycastPlane<FLOAT>;
|
||||||
|
void* userdata = mProps.raycastUserdata;
|
||||||
|
if (!callback) {
|
||||||
|
callback = fallback;
|
||||||
|
userdata = (void*) &mProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the ray misses, then try the fallback function.
|
||||||
|
FLOAT t;
|
||||||
|
if (!callback(mEye, dir, &t, userdata)) {
|
||||||
|
if (callback == fallback || !fallback(mEye, dir, &t, (void*) &mProps)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = mEye + dir * t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
filament::math::vec3<FLOAT> Manipulator<FLOAT>::raycastFarPlane(int x, int y) const {
|
||||||
|
const filament::math::vec3<FLOAT> gaze = normalize(mTarget - mEye);
|
||||||
|
const vec3 right = cross(gaze, mProps.upVector);
|
||||||
|
const vec3 upward = cross(right, gaze);
|
||||||
|
const FLOAT width = mProps.viewport[0];
|
||||||
|
const FLOAT height = mProps.viewport[1];
|
||||||
|
const FLOAT fov = mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
|
||||||
|
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
|
||||||
|
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
|
||||||
|
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
|
||||||
|
|
||||||
|
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
|
||||||
|
const FLOAT tangent = tan(fov / 2.0);
|
||||||
|
const FLOAT aspect = width / height;
|
||||||
|
|
||||||
|
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
|
||||||
|
vec3 dir = gaze;
|
||||||
|
if (mProps.fovDirection == Fov::VERTICAL) {
|
||||||
|
dir += right * tangent * u * aspect;
|
||||||
|
dir += upward * tangent * v;
|
||||||
|
} else {
|
||||||
|
dir += right * tangent * u;
|
||||||
|
dir += upward * tangent * v / aspect;
|
||||||
|
}
|
||||||
|
return mEye + dir * mProps.farPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::keyDown(Manipulator<FLOAT>::Key key) { }
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::keyUp(Manipulator<FLOAT>::Key key) { }
|
||||||
|
|
||||||
|
template <typename FLOAT>
|
||||||
|
void Manipulator<FLOAT>::update(FLOAT deltaTime) { }
|
||||||
|
|
||||||
|
template class Manipulator<float>;
|
||||||
|
template class Manipulator<double>;
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
197
macos/src/camutils/MapManipulator.h
Normal file
197
macos/src/camutils/MapManipulator.h
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAMUTILS_MAP_MANIPULATOR_H
|
||||||
|
#define CAMUTILS_MAP_MANIPULATOR_H
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/vec3.h>
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
class MapManipulator : public Manipulator<FLOAT> {
|
||||||
|
public:
|
||||||
|
using vec2 = math::vec2<FLOAT>;
|
||||||
|
using vec3 = math::vec3<FLOAT>;
|
||||||
|
using vec4 = math::vec4<FLOAT>;
|
||||||
|
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||||
|
using Base = Manipulator<FLOAT>;
|
||||||
|
using Config = typename Manipulator<FLOAT>::Config;
|
||||||
|
|
||||||
|
MapManipulator(Mode mode, const Config& props) : Manipulator<FLOAT>(mode, props) {
|
||||||
|
const FLOAT width = Base::mProps.mapExtent.x;
|
||||||
|
const FLOAT height = Base::mProps.mapExtent.y;
|
||||||
|
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const FLOAT halfExtent = (horiz ? width : height) / 2.0;
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT distance = halfExtent / tan(fov / 2.0);
|
||||||
|
Base::mTarget = Base::mProps.targetPosition;
|
||||||
|
Base::mEye = Base::mTarget + distance * targetToEye;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabBegin(int x, int y, bool strafe) override {
|
||||||
|
if (strafe || !Base::raycast(x, y, &mGrabScene)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mGrabFar = Base::raycastFarPlane(x, y);
|
||||||
|
mGrabEye = Base::mEye;
|
||||||
|
mGrabTarget = Base::mTarget;
|
||||||
|
mGrabbing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabUpdate(int x, int y) override {
|
||||||
|
if (mGrabbing) {
|
||||||
|
const FLOAT ulen = distance(mGrabScene, mGrabEye);
|
||||||
|
const FLOAT vlen = distance(mGrabFar, mGrabScene);
|
||||||
|
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
|
||||||
|
const vec3 eyePosition = mGrabEye + translation;
|
||||||
|
const vec3 targetPosition = mGrabTarget + translation;
|
||||||
|
moveWithConstraints(eyePosition, targetPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabEnd() override {
|
||||||
|
mGrabbing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(int x, int y, FLOAT scrolldelta) override {
|
||||||
|
vec3 grabScene;
|
||||||
|
if (!Base::raycast(x, y, &grabScene)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the direction of travel for the dolly. We do not normalize since it
|
||||||
|
// is desirable to move faster when further away from the targetPosition.
|
||||||
|
vec3 u = grabScene - Base::mEye;
|
||||||
|
|
||||||
|
// Prevent getting stuck when zooming in.
|
||||||
|
if (scrolldelta < 0) {
|
||||||
|
const FLOAT distanceToSurface = length(u);
|
||||||
|
if (distanceToSurface < Base::mProps.zoomSpeed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u *= -scrolldelta * Base::mProps.zoomSpeed;
|
||||||
|
|
||||||
|
const vec3 eyePosition = Base::mEye + u;
|
||||||
|
const vec3 targetPosition = Base::mTarget + u;
|
||||||
|
moveWithConstraints(eyePosition, targetPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getCurrentBookmark() const override {
|
||||||
|
const vec3 dir = normalize(Base::mTarget - Base::mEye);
|
||||||
|
|
||||||
|
FLOAT distance;
|
||||||
|
raycastPlane(Base::mEye, dir, &distance);
|
||||||
|
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT halfExtent = distance * tan(fov / 2.0);
|
||||||
|
|
||||||
|
vec3 targetPosition = Base::mEye + dir * distance;
|
||||||
|
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
|
||||||
|
const vec3 vvec = cross(targetToEye, uvec);
|
||||||
|
const vec3 centerToTarget = targetPosition - Base::mProps.targetPosition;
|
||||||
|
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::MAP;
|
||||||
|
bookmark.map.extent = halfExtent * 2.0;
|
||||||
|
bookmark.map.center.x = dot(uvec, centerToTarget);
|
||||||
|
bookmark.map.center.y = dot(vvec, centerToTarget);
|
||||||
|
|
||||||
|
bookmark.orbit.theta = 0;
|
||||||
|
bookmark.orbit.phi = 0;
|
||||||
|
bookmark.orbit.pivot = Base::mProps.targetPosition +
|
||||||
|
uvec * bookmark.map.center.x +
|
||||||
|
vvec * bookmark.map.center.y;
|
||||||
|
bookmark.orbit.distance = halfExtent / tan(fov / 2.0);
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getHomeBookmark() const override {
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT width = Base::mProps.mapExtent.x;
|
||||||
|
const FLOAT height = Base::mProps.mapExtent.y;
|
||||||
|
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
|
||||||
|
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::MAP;
|
||||||
|
bookmark.map.extent = horiz ? width : height;
|
||||||
|
bookmark.map.center.x = 0;
|
||||||
|
bookmark.map.center.y = 0;
|
||||||
|
|
||||||
|
bookmark.orbit.theta = 0;
|
||||||
|
bookmark.orbit.phi = 0;
|
||||||
|
bookmark.orbit.pivot = Base::mTarget;
|
||||||
|
bookmark.orbit.distance = 0.5 * bookmark.map.extent / tan(fov / 2.0);
|
||||||
|
|
||||||
|
// TODO: Add optional boundary constraints here.
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpToBookmark(const Bookmark& bookmark) override {
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const FLOAT halfExtent = bookmark.map.extent / 2.0;
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT distance = halfExtent / tan(fov / 2.0);
|
||||||
|
vec3 uvec = cross(Base::mProps.upVector, targetToEye);
|
||||||
|
vec3 vvec = cross(targetToEye, uvec);
|
||||||
|
uvec = normalize(uvec) * bookmark.map.center.x;
|
||||||
|
vvec = normalize(vvec) * bookmark.map.center.y;
|
||||||
|
Base::mTarget = Base::mProps.targetPosition + uvec + vvec;
|
||||||
|
Base::mEye = Base::mTarget + distance * targetToEye;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool raycastPlane(const vec3& origin, const vec3& dir, FLOAT* t) const {
|
||||||
|
const vec4 plane = Base::mProps.groundPlane;
|
||||||
|
const vec3 n = vec3(plane[0], plane[1], plane[2]);
|
||||||
|
const vec3 p0 = n * plane[3];
|
||||||
|
const FLOAT denom = -dot(n, dir);
|
||||||
|
if (denom > 1e-6) {
|
||||||
|
const vec3 p0l0 = p0 - origin;
|
||||||
|
*t = dot(p0l0, n) / -denom;
|
||||||
|
return *t >= 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveWithConstraints(vec3 eye, vec3 targetPosition) {
|
||||||
|
Base::mEye = eye;
|
||||||
|
Base::mTarget = targetPosition;
|
||||||
|
// TODO: Add optional boundary constraints here.
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mGrabbing = false;
|
||||||
|
vec3 mGrabScene;
|
||||||
|
vec3 mGrabFar;
|
||||||
|
vec3 mGrabEye;
|
||||||
|
vec3 mGrabTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
|
|
||||||
|
#endif /* CAMUTILS_MAP_MANIPULATOR_H */
|
||||||
201
macos/src/camutils/OrbitManipulator.h
Normal file
201
macos/src/camutils/OrbitManipulator.h
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAMUTILS_ORBIT_MANIPULATOR_H
|
||||||
|
#define CAMUTILS_ORBIT_MANIPULATOR_H
|
||||||
|
|
||||||
|
#include <camutils/Manipulator.h>
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
|
||||||
|
#define MAX_PHI (F_PI / 2.0 - 0.001)
|
||||||
|
|
||||||
|
namespace filament {
|
||||||
|
namespace camutils {
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
|
||||||
|
template<typename FLOAT>
|
||||||
|
class OrbitManipulator : public Manipulator<FLOAT> {
|
||||||
|
public:
|
||||||
|
using vec2 = filament::math::vec2<FLOAT>;
|
||||||
|
using vec3 = filament::math::vec3<FLOAT>;
|
||||||
|
using vec4 = filament::math::vec4<FLOAT>;
|
||||||
|
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||||
|
using Base = Manipulator<FLOAT>;
|
||||||
|
using Config = typename Base::Config;
|
||||||
|
|
||||||
|
enum GrabState { INACTIVE, ORBITING, PANNING };
|
||||||
|
|
||||||
|
OrbitManipulator(Mode mode, const Config& props) : Base(mode, props) {
|
||||||
|
setProperties(props);
|
||||||
|
Base::mEye = Base::mProps.orbitHomePosition;
|
||||||
|
mPivot = Base::mTarget = Base::mProps.targetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProperties(const Config& props) override {
|
||||||
|
Config resolved = props;
|
||||||
|
|
||||||
|
if (resolved.orbitHomePosition == vec3(0)) {
|
||||||
|
resolved.orbitHomePosition = vec3(0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolved.orbitSpeed == vec2(0)) {
|
||||||
|
resolved.orbitSpeed = vec2(0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, place the ground plane so that it aligns with the targetPosition position.
|
||||||
|
// This is used only when PANNING.
|
||||||
|
if (resolved.groundPlane == vec4(0)) {
|
||||||
|
const FLOAT d = length(resolved.targetPosition);
|
||||||
|
const vec3 n = normalize(resolved.orbitHomePosition - resolved.targetPosition);
|
||||||
|
resolved.groundPlane = vec4(n, -d);
|
||||||
|
}
|
||||||
|
|
||||||
|
Base::setProperties(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabBegin(int x, int y, bool strafe) override {
|
||||||
|
mGrabState = strafe ? PANNING : ORBITING;
|
||||||
|
mGrabPivot = mPivot;
|
||||||
|
mGrabEye = Base::mEye;
|
||||||
|
mGrabTarget = Base::mTarget;
|
||||||
|
mGrabBookmark = getCurrentBookmark();
|
||||||
|
mGrabWinX = x;
|
||||||
|
mGrabWinY = y;
|
||||||
|
mGrabFar = Base::raycastFarPlane(x, y);
|
||||||
|
Base::raycast(x, y, &mGrabScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabUpdate(int x, int y) override {
|
||||||
|
const int delx = mGrabWinX - x;
|
||||||
|
const int dely = mGrabWinY - y;
|
||||||
|
|
||||||
|
if (mGrabState == ORBITING) {
|
||||||
|
Bookmark bookmark = getCurrentBookmark();
|
||||||
|
|
||||||
|
const FLOAT theta = delx * Base::mProps.orbitSpeed.x;
|
||||||
|
const FLOAT phi = dely * Base::mProps.orbitSpeed.y;
|
||||||
|
const FLOAT maxPhi = MAX_PHI;
|
||||||
|
|
||||||
|
bookmark.orbit.phi = clamp(mGrabBookmark.orbit.phi + phi, -maxPhi, +maxPhi);
|
||||||
|
bookmark.orbit.theta = mGrabBookmark.orbit.theta + theta;
|
||||||
|
|
||||||
|
jumpToBookmark(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mGrabState == PANNING) {
|
||||||
|
const FLOAT ulen = distance(mGrabScene, mGrabEye);
|
||||||
|
const FLOAT vlen = distance(mGrabFar, mGrabScene);
|
||||||
|
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
|
||||||
|
mPivot = mGrabPivot + translation;
|
||||||
|
Base::mEye = mGrabEye + translation;
|
||||||
|
Base::mTarget = mGrabTarget + translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grabEnd() override {
|
||||||
|
mGrabState = INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(int x, int y, FLOAT scrolldelta) override {
|
||||||
|
const vec3 gaze = normalize(Base::mTarget - Base::mEye);
|
||||||
|
const vec3 movement = gaze * Base::mProps.zoomSpeed * -scrolldelta;
|
||||||
|
const vec3 v0 = mPivot - Base::mEye;
|
||||||
|
Base::mEye += movement;
|
||||||
|
Base::mTarget += movement;
|
||||||
|
const vec3 v1 = mPivot - Base::mEye;
|
||||||
|
|
||||||
|
// Check if the camera has moved past the point of interest.
|
||||||
|
if (dot(v0, v1) < 0) {
|
||||||
|
mFlipped = !mFlipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getCurrentBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::ORBIT;
|
||||||
|
const vec3 pivotToEye = Base::mEye - mPivot;
|
||||||
|
const FLOAT d = length(pivotToEye);
|
||||||
|
const FLOAT x = pivotToEye.x / d;
|
||||||
|
const FLOAT y = pivotToEye.y / d;
|
||||||
|
const FLOAT z = pivotToEye.z / d;
|
||||||
|
|
||||||
|
bookmark.orbit.phi = asin(y);
|
||||||
|
bookmark.orbit.theta = atan2(x, z);
|
||||||
|
bookmark.orbit.distance = mFlipped ? -d : d;
|
||||||
|
bookmark.orbit.pivot = mPivot;
|
||||||
|
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT halfExtent = d * tan(fov / 2.0);
|
||||||
|
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
|
||||||
|
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
|
||||||
|
const vec3 vvec = cross(targetToEye, uvec);
|
||||||
|
const vec3 centerToTarget = mPivot - Base::mProps.targetPosition;
|
||||||
|
|
||||||
|
bookmark.map.extent = halfExtent * 2;
|
||||||
|
bookmark.map.center.x = dot(uvec, centerToTarget);
|
||||||
|
bookmark.map.center.y = dot(vvec, centerToTarget);
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bookmark getHomeBookmark() const override {
|
||||||
|
Bookmark bookmark;
|
||||||
|
bookmark.mode = Mode::ORBIT;
|
||||||
|
bookmark.orbit.phi = FLOAT(0);
|
||||||
|
bookmark.orbit.theta = FLOAT(0);
|
||||||
|
bookmark.orbit.pivot = Base::mProps.targetPosition;
|
||||||
|
bookmark.orbit.distance = distance(Base::mProps.targetPosition, Base::mProps.orbitHomePosition);
|
||||||
|
|
||||||
|
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
|
||||||
|
const FLOAT halfExtent = bookmark.orbit.distance * tan(fov / 2.0);
|
||||||
|
|
||||||
|
bookmark.map.extent = halfExtent * 2;
|
||||||
|
bookmark.map.center.x = 0;
|
||||||
|
bookmark.map.center.y = 0;
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpToBookmark(const Bookmark& bookmark) override {
|
||||||
|
mPivot = bookmark.orbit.pivot;
|
||||||
|
const FLOAT x = sin(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
|
||||||
|
const FLOAT y = sin(bookmark.orbit.phi);
|
||||||
|
const FLOAT z = cos(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
|
||||||
|
Base::mEye = mPivot + vec3(x, y, z) * abs(bookmark.orbit.distance);
|
||||||
|
mFlipped = bookmark.orbit.distance < 0;
|
||||||
|
Base::mTarget = Base::mEye + vec3(x, y, z) * (mFlipped ? 1.0 : -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GrabState mGrabState = INACTIVE;
|
||||||
|
bool mFlipped = false;
|
||||||
|
vec3 mGrabPivot;
|
||||||
|
vec3 mGrabScene;
|
||||||
|
vec3 mGrabFar;
|
||||||
|
vec3 mGrabEye;
|
||||||
|
vec3 mGrabTarget;
|
||||||
|
Bookmark mGrabBookmark;
|
||||||
|
int mGrabWinX;
|
||||||
|
int mGrabWinY;
|
||||||
|
vec3 mPivot;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace camutils
|
||||||
|
} // namespace filament
|
||||||
|
|
||||||
|
#endif /* CAMUTILS_ORBIT_MANIPULATOR_H */
|
||||||
Reference in New Issue
Block a user