Files
filament/samples/app/FilamentApp.cpp
2018-08-27 08:51:46 -07:00

691 lines
25 KiB
C++

#include <memory>
/*
* Copyright (C) 2015 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 "FilamentApp.h"
#if !defined(WIN32)
# include <unistd.h>
#else
# include <SDL_syswm.h>
# include <utils/unwindows.h>
#endif
#include <imgui.h>
#include <utils/Panic.h>
#include <utils/Path.h>
#include <filament/Camera.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/Renderer.h>
#include <filament/RenderableManager.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/View.h>
#include <filagui/ImGuiHelper.h>
#include "Cube.h"
#include "NativeWindowHelper.h"
using namespace filament;
using namespace filagui;
using namespace math;
using namespace utils;
static constexpr uint8_t AI_DEFAULT_MAT_PACKAGE[] = {
#include "generated/material/aiDefaultMat.inc"
};
static constexpr uint8_t TRANSPARENT_COLOR_PACKAGE[] = {
#include "generated/material/transparentColor.inc"
};
static constexpr uint8_t DEPTH_VISUALIZER_PACKAGE[] = {
#include "generated/material/depthVisualizer.inc"
};
FilamentApp& FilamentApp::get() {
static FilamentApp filamentApp;
return filamentApp;
}
FilamentApp::FilamentApp() {
initSDL();
}
FilamentApp::~FilamentApp() {
SDL_Quit();
}
void FilamentApp::run(const Config& config, SetupCallback setupCallback,
CleanupCallback cleanupCallback, ImGuiCallback imguiCallback,
PreRenderCallback preRender, PostRenderCallback postRender,
size_t width, size_t height) {
std::unique_ptr<FilamentApp::Window> window(
new FilamentApp::Window(this, config, config.title, width, height));
mDepthMaterial = Material::Builder()
.package((void*) DEPTH_VISUALIZER_PACKAGE, sizeof(DEPTH_VISUALIZER_PACKAGE))
.build(*mEngine);
mDepthMI = mDepthMaterial->createInstance();
mDefaultMaterial = Material::Builder()
.package((void*) AI_DEFAULT_MAT_PACKAGE, sizeof(AI_DEFAULT_MAT_PACKAGE))
.build(*mEngine);
mTransparentMaterial = Material::Builder()
.package((void*) TRANSPARENT_COLOR_PACKAGE, sizeof(TRANSPARENT_COLOR_PACKAGE))
.build(*mEngine);
std::unique_ptr<Cube> cameraCube(new Cube(*mEngine, mTransparentMaterial, {1,0,0}));
// we can't cull the light-frustum because it's not applied a rigid transform
// and currently, filament assumes that for culling
std::unique_ptr<Cube> lightmapCube(new Cube(*mEngine, mTransparentMaterial, {0,1,0}, false));
mScene = mEngine->createScene();
window->mMainView->getView()->setVisibleLayers(0x4, 0x4);
window->mUiView->getView()->setClearTargets(false, false, false);
window->mUiView->getView()->setRenderTarget(View::TargetBufferFlags::DEPTH_AND_STENCIL);
window->mUiView->getView()->setPostProcessingEnabled(false);
window->mUiView->getView()->setShadowsEnabled(false);
if (config.splitView) {
auto& rcm = mEngine->getRenderableManager();
rcm.setLayerMask(rcm.getInstance(cameraCube->getSolidRenderable()), 0x3, 0x2);
rcm.setLayerMask(rcm.getInstance(cameraCube->getWireFrameRenderable()), 0x3, 0x2);
cameraCube->mapFrustum(*mEngine, window->mMainCameraMan.getCamera());
rcm.setLayerMask(rcm.getInstance(lightmapCube->getSolidRenderable()), 0x3, 0x2);
rcm.setLayerMask(rcm.getInstance(lightmapCube->getWireFrameRenderable()), 0x3, 0x2);
// Create the camera mesh
window->mMainCameraMan.setCameraChangedCallback(
[&cameraCube, &lightmapCube, &window, engine = mEngine](Camera const* camera) {
cameraCube->mapFrustum(*engine, camera);
lightmapCube->mapFrustum(*engine,
window->mMainView->getView()->getDirectionalLightCamera());
});
mScene->addEntity(cameraCube->getWireFrameRenderable());
mScene->addEntity(cameraCube->getSolidRenderable());
mScene->addEntity(lightmapCube->getWireFrameRenderable());
mScene->addEntity(lightmapCube->getSolidRenderable());
window->mGodView->getView()->setVisibleLayers(0x6, 0x6);
window->mOrthoView->getView()->setVisibleLayers(0x6, 0x6);
// only preserve the color buffer for additional views; depth and stencil can be discarded.
window->mDepthView->getView()->setRenderTarget(View::TargetBufferFlags::DEPTH_AND_STENCIL);
window->mGodView->getView()->setRenderTarget(View::TargetBufferFlags::DEPTH_AND_STENCIL);
window->mOrthoView->getView()->setRenderTarget(View::TargetBufferFlags::DEPTH_AND_STENCIL);
window->mDepthView->getView()->setShadowsEnabled(false);
window->mGodView->getView()->setShadowsEnabled(false);
window->mOrthoView->getView()->setShadowsEnabled(false);
}
loadIBL(config);
if (mIBL != nullptr) {
mIBL->getSkybox()->setLayerMask(0x7, 0x4);
mScene->setSkybox(mIBL->getSkybox());
mScene->setIndirectLight(mIBL->getIndirectLight());
}
for (auto& view : window->mViews) {
if (view.get() != window->mUiView) {
view->getView()->setScene(mScene);
}
}
setupCallback(mEngine, window->mMainView->getView(), mScene);
if (imguiCallback) {
mImGuiHelper = std::make_unique<ImGuiHelper>(mEngine, window->mUiView->getView(),
getRootPath() + "assets/fonts/Roboto-Medium.ttf");
ImGuiIO& io = ImGui::GetIO();
#ifdef WIN32
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window->getSDLWindow(), &wmInfo);
io.ImeWindowHandle = wmInfo.info.win.window;
#endif
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
io.SetClipboardTextFn = [](void*, const char* text) {
SDL_SetClipboardText(text);
};
io.GetClipboardTextFn = [](void*) -> const char* {
return SDL_GetClipboardText();
};
io.ClipboardUserData = nullptr;
}
bool mousePressed[3] = { false };
while (!mClosed) {
if (!UTILS_HAS_THREADING) {
mEngine->execute();
}
// Allow the app to animate the scene if desired.
if (mAnimation) {
double now = (double) SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency();
mAnimation(mEngine, window->mMainView->getView(), now);
}
// Loop over fresh events twice: first stash them and let ImGui process them, then allow
// the app to process the stashed events. This is done because ImGui might wish to block
// certain events from the app (e.g., when dragging the mouse over an obscuring window).
constexpr int kMaxEvents = 16;
SDL_Event events[kMaxEvents];
int nevents = 0;
while (nevents < kMaxEvents && SDL_PollEvent(&events[nevents]) != 0) {
if (mImGuiHelper) {
ImGuiIO& io = ImGui::GetIO();
SDL_Event* event = &events[nevents];
switch (event->type) {
case SDL_MOUSEWHEEL: {
if (event->wheel.x > 0) io.MouseWheelH += 1;
if (event->wheel.x < 0) io.MouseWheelH -= 1;
if (event->wheel.y > 0) io.MouseWheel += 1;
if (event->wheel.y < 0) io.MouseWheel -= 1;
break;
}
case SDL_MOUSEBUTTONDOWN: {
if (event->button.button == SDL_BUTTON_LEFT) mousePressed[0] = true;
if (event->button.button == SDL_BUTTON_RIGHT) mousePressed[1] = true;
if (event->button.button == SDL_BUTTON_MIDDLE) mousePressed[2] = true;
break;
}
case SDL_TEXTINPUT: {
io.AddInputCharactersUTF8(event->text.text);
break;
}
case SDL_KEYDOWN:
case SDL_KEYUP: {
int key = event->key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
break;
}
}
}
nevents++;
}
// Now, loop over the events a second time for app-side processing.
for (int i = 0; i < nevents; i++) {
const SDL_Event& event = events[i];
ImGuiIO* io = mImGuiHelper ? &ImGui::GetIO() : nullptr;
switch (event.type) {
case SDL_QUIT:
mClosed = true;
break;
case SDL_KEYDOWN:
if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
mClosed = true;
}
break;
case SDL_MOUSEWHEEL:
if (!io || !io->WantCaptureMouse)
window->mouseWheel(event.wheel.y);
break;
case SDL_MOUSEBUTTONDOWN:
if (!io || !io->WantCaptureMouse)
window->mouseDown(event.button.button, event.button.x, event.button.y);
break;
case SDL_MOUSEBUTTONUP:
if (!io || !io->WantCaptureMouse)
window->mouseUp(event.button.x, event.button.y);
break;
case SDL_MOUSEMOTION:
if (!io || !io->WantCaptureMouse)
window->mouseMoved(event.motion.x, event.motion.y);
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
window->resize();
break;
default:
break;
}
break;
default:
break;
}
}
// Populate the UI scene, regardless of whether Filament wants to a skip frame. We should
// always let ImGui generate a command list; if it skips a frame it'll destroy its widgets.
if (mImGuiHelper) {
// Inform ImGui of the current window size in case it was resized.
int windowWidth, windowHeight;
int displayWidth, displayHeight;
SDL_GetWindowSize(window->mWindow, &windowWidth, &windowHeight);
SDL_GL_GetDrawableSize(window->mWindow, &displayWidth, &displayHeight);
mImGuiHelper->setDisplaySize(windowWidth, windowHeight,
windowWidth > 0 ? ((float)displayWidth / windowWidth) : 0,
displayHeight > 0 ? ((float)displayHeight / windowHeight) : 0);
// Setup mouse inputs (we already got mouse wheel, keyboard keys & characters
// from our event handler)
ImGuiIO& io = ImGui::GetIO();
int mx, my;
Uint32 buttons = SDL_GetMouseState(&mx, &my);
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
io.MouseDown[0] = mousePressed[0] || (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
io.MouseDown[1] = mousePressed[1] || (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = mousePressed[2] || (buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
mousePressed[0] = mousePressed[1] = mousePressed[2] = false;
// TODO: Update to a newer SDL and use SDL_CaptureMouse() to retrieve mouse coordinates
// outside of the client area; see the imgui SDL example.
if ((SDL_GetWindowFlags(window->mWindow) & SDL_WINDOW_INPUT_FOCUS) != 0) {
io.MousePos = ImVec2((float)mx, (float)my);
}
// Populate the UI Scene.
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 now = SDL_GetPerformanceCounter();
float timeStep = mTime > 0 ? (float)((double)(now - mTime) / frequency) :
(float)(1.0f / 60.0f);
mTime = now;
mImGuiHelper->render(timeStep, imguiCallback);
}
window->mMainCameraMan.updateCameraTransform();
// TODO: we need better timing or use SDL_GL_SetSwapInterval
SDL_Delay(16);
Renderer* renderer = window->getRenderer();
if (preRender) {
for (auto const& view : window->mViews) {
if (view.get() != window->mUiView) {
preRender(mEngine, view->getView(), mScene, renderer);
}
}
}
if (renderer->beginFrame(window->getSwapChain())) {
for (auto const& view : window->mViews) {
renderer->render(view->getView());
}
renderer->endFrame();
}
if (postRender) {
for (auto const& view : window->mViews) {
if (view.get() != window->mUiView) {
postRender(mEngine, view->getView(), mScene, renderer);
}
}
}
}
if (mImGuiHelper) {
mImGuiHelper.reset();
}
cleanupCallback(mEngine, window->mMainView->getView(), mScene);
cameraCube.reset();
lightmapCube.reset();
window.reset();
mIBL.reset();
mEngine->destroy(mDepthMI);
mEngine->destroy(mDepthMaterial);
mEngine->destroy(mDefaultMaterial);
mEngine->destroy(mTransparentMaterial);
mEngine->destroy(mScene);
Engine::destroy(&mEngine);
mEngine = nullptr;
}
void FilamentApp::loadIBL(const Config& config) {
if (!config.iblDirectory.empty()) {
Path iblPath(config.iblDirectory);
if (!iblPath.exists()) {
std::cerr << "The specified IBL path does not exist: " << iblPath << std::endl;
return;
}
if (!iblPath.isDirectory()) {
std::cerr << "The specified IBL path is not a directory: " << iblPath << std::endl;
return;
}
mIBL = std::make_unique<IBL>(*mEngine);
if (!mIBL->loadFromDirectory(iblPath)) {
std::cerr << "Could not load the specified IBL: " << iblPath << std::endl;
mIBL.reset(nullptr);
return;
}
}
}
void FilamentApp::initSDL() {
ASSERT_POSTCONDITION(SDL_Init(SDL_INIT_EVENTS) == 0, "SDL_Init Failure");
}
// ------------------------------------------------------------------------------------------------
FilamentApp::Window::Window(FilamentApp* filamentApp,
const Config& config, std::string title, size_t w, size_t h)
: mFilamentApp(filamentApp) {
const int x = SDL_WINDOWPOS_CENTERED;
const int y = SDL_WINDOWPOS_CENTERED;
const uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
mWindow = SDL_CreateWindow(title.c_str(), x, y, (int) w, (int) h, windowFlags);
// Create the Engine after the window in case this happens to be a single-threaded platform.
// For single-threaded platforms, we need to ensure that Filament's OpenGL context is current,
// rather than the one created by SDL.
mFilamentApp->mEngine = Engine::create(config.backend);
// HACK: We don't use SDL's 2D rendering functionality, but by invoking it we cause
// SDL to create a Metal backing layer, which allows us to run Vulkan apps via MoltenVK.
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) && defined(__APPLE__)
constexpr int METAL_DRIVER = 2;
SDL_CreateRenderer(mWindow, METAL_DRIVER, SDL_RENDERER_ACCELERATED);
#endif
void* nativeWindow = ::getNativeWindow(mWindow);
mSwapChain = mFilamentApp->mEngine->createSwapChain(nativeWindow);
mRenderer = mFilamentApp->mEngine->createRenderer();
// create cameras
mCameras[0] = mMainCamera = mFilamentApp->mEngine->createCamera();
mCameras[1] = mDebugCamera = mFilamentApp->mEngine->createCamera();
mCameras[2] = mOrthoCamera = mFilamentApp->mEngine->createCamera();
mCameras[3] = mUiCamera = mFilamentApp->mEngine->createCamera();
// set exposure
for (auto camera : mCameras) {
camera->setExposure(16.0f, 1 / 125.0f, 100.0f);
}
// create views
mViews.emplace_back(mMainView = new CView(*mRenderer, "Main View"));
if (config.splitView) {
mViews.emplace_back(mDepthView = new CView(*mRenderer, "Depth View"));
mViews.emplace_back(mGodView = new GodView(*mRenderer, "God View"));
mViews.emplace_back(mOrthoView = new CView(*mRenderer, "Ortho View"));
mDepthView->getView()->setDepthPrepass(View::DepthPrepass::DISABLED);
}
mViews.emplace_back(mUiView = new CView(*mRenderer, "UI View"));
// set-up the camera manipulators
double3 at(0, 0, -4);
mMainCameraMan.setCamera(mMainCamera);
mDebugCameraMan.setCamera(mDebugCamera);
mMainView->setCamera(mMainCamera);
mMainView->setCameraManipulator(&mMainCameraMan);
mUiView->setCamera(mUiCamera);
if (config.splitView) {
// Depth view always uses the main camera
mDepthView->setCamera(mMainCamera);
// The god view uses the main camera for culling, but the debug camera for viewing
mGodView->setCamera(mMainCamera);
mGodView->setGodCamera(mDebugCamera);
// Ortho view obviously uses an ortho camera
mOrthoView->setCamera( (Camera *)mMainView->getView()->getDirectionalLightCamera() );
mDepthView->setCameraManipulator(&mMainCameraMan);
mGodView->setCameraManipulator(&mDebugCameraMan);
mOrthoView->setCameraManipulator(&mOrthoCameraMan);
}
// configure the cameras
configureCamerasForWindow();
mMainCameraMan.lookAt(at + double3{ 0, 0, 4 }, at);
mDebugCameraMan.lookAt(at + double3{ 0, 0, 4 }, at);
mOrthoCameraMan.lookAt(at + double3{ 0, 0, 4 }, at);
}
FilamentApp::Window::~Window() {
mViews.clear();
for (auto& camera : mCameras) {
mFilamentApp->mEngine->destroy(camera);
}
mFilamentApp->mEngine->destroy(mRenderer);
mFilamentApp->mEngine->destroy(mSwapChain);
SDL_DestroyWindow(mWindow);
}
void FilamentApp::Window::mouseDown(int button, ssize_t x, ssize_t y) {
fixupMouseCoordinatesForHdpi(x, y);
y = mHeight - y;
for (auto const& view : mViews) {
if (view->intersects(x, y)) {
mEventTarget = view.get();
view->mouseDown(button, x, y);
break;
}
}
}
void FilamentApp::Window::mouseWheel(ssize_t x) {
if (mEventTarget) {
mEventTarget->mouseWheel(x);
} else {
for (auto const& view : mViews) {
if (view->intersects(mLastX, mLastY)) {
view->mouseWheel(x);
break;
}
}
}
}
void FilamentApp::Window::mouseUp(ssize_t x, ssize_t y) {
fixupMouseCoordinatesForHdpi(x, y);
if (mEventTarget) {
y = mHeight - y;
mEventTarget->mouseUp(x, y);
mEventTarget = nullptr;
}
}
void FilamentApp::Window::mouseMoved(ssize_t x, ssize_t y) {
fixupMouseCoordinatesForHdpi(x, y);
y = mHeight - y;
if (mEventTarget) {
mEventTarget->mouseMoved(x, y);
}
mLastX = x;
mLastY = y;
}
void FilamentApp::Window::fixupMouseCoordinatesForHdpi(ssize_t& x, ssize_t& y) const {
int dw, dh, ww, wh;
SDL_GL_GetDrawableSize(mWindow, &dw, &dh);
SDL_GetWindowSize(mWindow, &ww, &wh);
x = x * dw / ww;
y = y * dh / wh;
}
void FilamentApp::Window::resize() {
mFilamentApp->mEngine->destroy(mSwapChain);
mSwapChain = mFilamentApp->mEngine->createSwapChain(::getNativeWindow(mWindow));
configureCamerasForWindow();
}
void FilamentApp::Window::configureCamerasForWindow() {
// Determine the current size of the window in physical pixels.
uint32_t w, h;
SDL_GL_GetDrawableSize(mWindow, (int*) &w, (int*) &h);
mWidth = (size_t) w;
mHeight = (size_t) h;
// Compute the "virtual pixels to physical pixels" scale factor that the
// the platform uses for UI elements.
int virtualWidth, virtualHeight;
SDL_GetWindowSize(mWindow, &virtualWidth, &virtualHeight);
float dpiScaleX = (float) w / virtualWidth;
float dpiScaleY = (float) h / virtualHeight;
const float3 at(0, 0, -4);
const double ratio = double(h) / double(w);
double near = 0.1;
double far = 50;
mMainCamera->setProjection(45.0, double(w) / h, near, far, Camera::Fov::VERTICAL);
mDebugCamera->setProjection(45.0, double(w) / h, 0.0625, 4096, Camera::Fov::VERTICAL);
mOrthoCamera->setProjection(Camera::Projection::ORTHO, -3, 3, -3 * ratio, 3 * ratio, near, far);
mOrthoCamera->lookAt(at + float3{ 4, 0, 0 }, at);
mUiCamera->setProjection(Camera::Projection::ORTHO,
0.0, w / dpiScaleX,
h / dpiScaleY, 0.0,
0.0, 1.0);
// We're in split view when there are more views than just the Main and UI views.
if (mViews.size() > 2) {
uint32_t vpw = w / 2;
uint32_t vph = h / 2;
mMainView->setViewport ({ 0, 0, vpw, vph });
mDepthView->setViewport({ int32_t(vpw), 0, w - vpw, vph });
mGodView->setViewport ({ int32_t(vpw), int32_t(vph), w - vpw, h - vph });
mOrthoView->setViewport({ 0, int32_t(vph), vpw, h - vph });
mMainView->getCameraManipulator()->updateCameraTransform();
mDepthView->getCameraManipulator()->updateCameraTransform();
mGodView->getCameraManipulator()->updateCameraTransform();
mOrthoView->getCameraManipulator()->updateCameraTransform();
} else {
mMainView->setViewport({ 0, 0, w, h });
}
mUiView->setViewport({ 0, 0, w, h });
}
// ------------------------------------------------------------------------------------------------
FilamentApp::CView::CView(Renderer& renderer, std::string name)
: engine(*renderer.getEngine()), mName(name) {
view = engine.createView();
view->setClearColor({ 0 });
view->setName(name.c_str());
}
FilamentApp::CView::~CView() {
engine.destroy(view);
}
void FilamentApp::CView::setViewport(Viewport const& viewport) {
mViewport = viewport;
view->setViewport(viewport);
if (mCameraManipulator) {
mCameraManipulator->setViewport(viewport.width, viewport.height);
}
}
void FilamentApp::CView::mouseDown(int button, ssize_t x, ssize_t y) {
mLastMousePosition = double2(x, y);
if (button == 1) {
mMode = Mode::ROTATE;
} else if (button == 3) {
mMode = Mode::TRACK;
}
}
void FilamentApp::CView::mouseUp(ssize_t x, ssize_t y) {
mMode = Mode::NONE;
}
void FilamentApp::CView::mouseMoved(ssize_t x, ssize_t y) {
if (mCameraManipulator) {
double2 delta = double2(x, y) - mLastMousePosition;
mLastMousePosition = double2(x, y);
switch (mMode) {
case Mode::NONE:
break;
case Mode::ROTATE:
mCameraManipulator->rotate(delta);
break;
case Mode::TRACK:
mCameraManipulator->track(delta);
break;
}
}
}
void FilamentApp::CView::mouseWheel(ssize_t x) {
if (mCameraManipulator){
mCameraManipulator->dolly(x);
}
}
bool FilamentApp::CView::intersects(ssize_t x, ssize_t y) {
if (x >= mViewport.left && x < mViewport.left + mViewport.width)
if (y >= mViewport.bottom && y < mViewport.bottom + mViewport.height)
return true;
return false;
}
void FilamentApp::CView::setCameraManipulator(CameraManipulator* cm) {
mCameraManipulator = cm;
}
void FilamentApp::CView::setCamera(Camera* camera) {
view->setCamera(camera);
}
void FilamentApp::GodView::setGodCamera(Camera* camera) {
getView()->setDebugCamera(camera);
}