diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 09c7181dc..b45ff3283 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -13,6 +13,11 @@ if(BRAYNS_NETWORKING_ENABLED) add_subdirectory(Rockets) endif() +option(BRAYNS_MULTIVIEW_ENABLED "Activate Multiview camera plugin" OFF) +if(BRAYNS_MULTIVIEW_ENABLED) + add_subdirectory(Multiview) +endif() + option(BRAYNS_OPENDECK_ENABLED "Activate OpenDeck plugin" OFF) if(BRAYNS_OPENDECK_ENABLED) add_subdirectory(OpenDeck) diff --git a/plugins/Multiview/CMakeLists.txt b/plugins/Multiview/CMakeLists.txt new file mode 100644 index 000000000..5456e28f1 --- /dev/null +++ b/plugins/Multiview/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (c) 2018, EPFL/Blue Brain Project +# All rights reserved. Do not distribute without permission. +# Responsible Author: Grigori Chevtchenko +# +# This file is part of Brayns + +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + +project(BraynsMultiview VERSION 0.1.0) +set(BraynsMultiview_VERSION_ABI 1) + +include(Common) + +common_find_package(ospray 1.7 SYSTEM) +common_find_package_post() + +set(BRAYNSMULTIVIEW_HEADERS MultiviewPlugin.h) +set(BRAYNSMULTIVIEW_SOURCES MultiviewPlugin.cpp) +set(BRAYNSMULTIVIEW_LINK_LIBRARIES PRIVATE braynsCommon braynsEngine braynsPluginAPI) + +if(OSPRAY_FOUND) + list(APPEND BRAYNSMULTIVIEW_SOURCES + ispc/multiview/MultiviewCamera.cpp) + + set(BRAYNSMULTIVIEW_ISPC_SOURCES + ispc/multiview/MultiviewCamera.ispc) + + list(APPEND BRAYNSMULTIVIEW_SOURCES ${BRAYNSMULTIVIEW_ISPC_SOURCES}) + + # reuse ispc setup and macros from ospray + list(APPEND CMAKE_MODULE_PATH ${OSPRAY_CMAKE_ROOT}) + if(CMAKE_BUILD_TYPE STREQUAL Debug) + set(OSPRAY_DEBUG_BUILD ON) + endif() + include(ispc) + + # Compile ispc code + include_directories_ispc(${PROJECT_SOURCE_DIR}/../../ ${OSPRAY_INCLUDE_DIRS}) + ospray_ispc_compile(${BRAYNSMULTIVIEW_ISPC_SOURCES}) + list(APPEND BRAYNSMULTIVIEW_SOURCES ${ISPC_OBJECTS}) + + list(APPEND BRAYNSMULTIVIEW_LINK_LIBRARIES ${OSPRAY_LIBRARIES}) +endif() + +set(BRAYNSMULTIVIEW_OMIT_LIBRARY_HEADER ON) +set(BRAYNSMULTIVIEW_OMIT_VERSION_HEADERS ON) +set(BRAYNSMULTIVIEW_OMIT_EXPORT ON) +set(BRAYNSMULTIVIEW_INCLUDE_NAME brayns_multiview) +common_library(braynsMultiview) + +if (TARGET Brayns-all) + add_dependencies(Brayns-all braynsMultiview) +endif() diff --git a/plugins/Multiview/MultiviewPlugin.cpp b/plugins/Multiview/MultiviewPlugin.cpp new file mode 100644 index 000000000..bf8aa87bf --- /dev/null +++ b/plugins/Multiview/MultiviewPlugin.cpp @@ -0,0 +1,74 @@ +/* Copyright (c) 2018, EPFL/Blue Brain Project + * All rights reserved. Do not distribute without permission. + * + * This file is part of Brayns + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "MultiviewPlugin.h" + +#include +#include +#include +#include +#include + +constexpr auto PARAM_ARM_LENGTH = "armLength"; + +namespace brayns +{ + +MultiviewPlugin::MultiviewPlugin(PropertyMap&& properties) + : _properties(std::move(properties)) +{ + const double armLength = _properties.getProperty(PARAM_ARM_LENGTH); + if (armLength <= 0.0f) + { + throw std::runtime_error( + "The multiview camera arm length must be stricly positive"); + } +} + +void MultiviewPlugin::init() +{ + auto& engine = _api->getEngine(); + + auto& params = engine.getParametersManager(); + if(params.getApplicationParameters().getEngine() == "ospray") + engine.addCameraType("multiview", _properties); + else + throw std::runtime_error("The multiview camera is only available for ospray engine"); +} +} + +extern "C" brayns::ExtensionPlugin* brayns_plugin_create(const int argc, + const char** argv) +{ + brayns::PropertyMap properties; + properties.setProperty({PARAM_ARM_LENGTH, 5.0, brayns::Property::MetaData{"Cameras arm length", + "The distance between the cameras and the view center"}}); + + if (!properties.parse(argc, argv)) + return nullptr; + try + { + return new brayns::MultiviewPlugin(std::move(properties)); + } + catch (const std::runtime_error& exc) + { + std::cerr << exc.what() << std::endl; + return nullptr; + } +} diff --git a/plugins/Multiview/MultiviewPlugin.h b/plugins/Multiview/MultiviewPlugin.h new file mode 100644 index 000000000..f8c152740 --- /dev/null +++ b/plugins/Multiview/MultiviewPlugin.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2018, EPFL/Blue Brain Project + * All rights reserved. Do not distribute without permission. + * + * This file is part of Brayns + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include +#include +#include + +namespace brayns +{ +class MultiviewPlugin : public ExtensionPlugin +{ +public: + MultiviewPlugin(PropertyMap&& properties); + + void init() final; + +private: + PropertyMap _properties; +}; +} diff --git a/plugins/Multiview/README.md b/plugins/Multiview/README.md new file mode 100644 index 000000000..81da8f9fa --- /dev/null +++ b/plugins/Multiview/README.md @@ -0,0 +1,15 @@ +The Multiview Module for Brayns +================================== + +This module implements the multiview(top/front/right/perspective) camera for Brayns + +Usage +----- + +- Point LD_LIBRARY_PATH to the folder which contains + 'libospray_module_multiview.so' +- Run Brayns application either with command line '--module multiview --camera-type multiview' or do + 'ospLoadModule("multiview")' programmatically +``` +OSPCamera camera = ospNewCamera("multiview"); +``` diff --git a/plugins/Multiview/ispc/multiview/MultiviewCamera.cpp b/plugins/Multiview/ispc/multiview/MultiviewCamera.cpp new file mode 100644 index 000000000..04fe17419 --- /dev/null +++ b/plugins/Multiview/ispc/multiview/MultiviewCamera.cpp @@ -0,0 +1,88 @@ +/* Copyright (c) 2018, EPFL/Blue Brain Project + * All rights reserved. Do not distribute without permission. + * Responsible Author: Grigori Chevtchenko + * + * This file is part of Brayns + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "MultiviewCamera.h" +#include "MultiviewCamera_ispc.h" + +#include + +namespace ospray +{ +MultiviewCamera::MultiviewCamera() +{ + ispcEquivalent = ispc::MultiviewCamera_create(this); +} + +std::string MultiviewCamera::toString() const +{ + return "ospray::MultiviewCamera"; +} + +void MultiviewCamera::commit() +{ + Camera::commit(); + + const float fovy = getParamf("fovy", 60.f); + const float aspect = getParamf("aspect", 1.66667f); + const float apertureRadius = getParamf("apertureRadius", 0.f); + const float focusDistance = getParamf("focusDistance", 1.f); + const float height = getParamf("height", 1.f); // imgPlane_size_y + const float armLength = getParamf("armLength", 5.f); + + clipPlanes = getParamData("clipPlanes", nullptr); + + dir = normalize(dir); + vec3f dir_du = normalize(cross(dir, up)); + vec3f dir_dv = cross(dir_du, dir); + + vec3f org = pos; + + float imgPlane_size_y = 2.f * tanf(deg2rad(0.5f * fovy)); + float imgPlane_size_x = imgPlane_size_y * aspect; + + dir_du *= imgPlane_size_x; + dir_dv *= imgPlane_size_y; + + vec3f dir_00 = dir - 0.5f * dir_du - 0.5f * dir_dv; + + float scaledAperture = 0.f; + // prescale to focal plane + if (apertureRadius > 0.f) + { + dir_du *= focusDistance; + dir_dv *= focusDistance; + dir_00 *= focusDistance; + scaledAperture = apertureRadius / imgPlane_size_x; + } + + const auto clipPlaneData = clipPlanes ? clipPlanes->data : nullptr; + const size_t numClipPlanes = clipPlanes ? clipPlanes->numItems : 0; + + ispc::MultiviewCamera_set(getIE(), (const ispc::vec3f&)org, + (const ispc::vec3f&)dir_00, + (const ispc::vec3f&)dir_du, + (const ispc::vec3f&)dir_dv, scaledAperture, + height, aspect, armLength, + (const ispc::vec4f*)clipPlaneData, + numClipPlanes); +} + +OSP_REGISTER_CAMERA(MultiviewCamera, multiview); +} diff --git a/plugins/Multiview/ispc/multiview/MultiviewCamera.h b/plugins/Multiview/ispc/multiview/MultiviewCamera.h new file mode 100644 index 000000000..6e8a66499 --- /dev/null +++ b/plugins/Multiview/ispc/multiview/MultiviewCamera.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2018, EPFL/Blue Brain Project + * All rights reserved. Do not distribute without permission. + * Responsible Author: Grigori Chevtchenko + * + * This file is part of Brayns + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +namespace ospray +{ +/** + * This is a 4 view camera. You can see from the top, + * right, front and perspective viewports. + */ +struct OSPRAY_SDK_INTERFACE MultiviewCamera : public Camera +{ + MultiviewCamera(); + std::string toString() const override; + void commit() override; + + // Clip planes + Ref clipPlanes; +}; +} diff --git a/plugins/Multiview/ispc/multiview/MultiviewCamera.ih b/plugins/Multiview/ispc/multiview/MultiviewCamera.ih new file mode 100644 index 000000000..d59afb9bd --- /dev/null +++ b/plugins/Multiview/ispc/multiview/MultiviewCamera.ih @@ -0,0 +1,41 @@ +/* Copyright (c) 2018, EPFL/Blue Brain Project + * All rights reserved. Do not distribute without permission. + * Responsible Author: Grigori Chevtchenko + * + * This file is part of Brayns + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +struct MultiviewCamera +{ + Camera super; + + vec3f org; + vec3f dir_00; + vec3f dir_du; + vec3f dir_dv; + float scaledAperture; + float height; + float aspect; + float armLength; + + // Clip planes + const uniform vec4f* clipPlanes; + unsigned int numClipPlanes; +}; diff --git a/plugins/Multiview/ispc/multiview/MultiviewCamera.ispc b/plugins/Multiview/ispc/multiview/MultiviewCamera.ispc new file mode 100644 index 000000000..c1a1aee1b --- /dev/null +++ b/plugins/Multiview/ispc/multiview/MultiviewCamera.ispc @@ -0,0 +1,159 @@ +/* Copyright (c) 2018, EPFL/Blue Brain Project + * All rights reserved. Do not distribute without permission. + * Responsible Author: Grigori Chevtchenko + * + * This file is part of Brayns + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "MultiviewCamera.ih" +#include "engines/ospray/ispc/camera/utils.ih" +#include "math/LinearSpace.ih" +#include "math/math.ih" +#include "math/sampling.ih" + +void MultiviewCamera_initRay(uniform Camera* uniform _self, varying Ray& ray, + const varying CameraSample& sample) +{ + uniform MultiviewCamera* uniform self = + (uniform MultiviewCamera * uniform)_self; + + vec2f screen = Camera_subRegion(_self, sample.screen); + + if(screen.x < 0.5f && screen.y < 0.5f) + { + //left + const vec3f dir = make_vec3f(1.0f, 0.0f, 0.0f); + const vec3f pos_du = make_vec3f(0.0f, 0.0f, 1.0f) * self->height * self->aspect; + const vec3f pos_dv = make_vec3f(0.0f, 1.0f, 0.0f) * self->height; + const vec3f pos_00 = (self->org + make_vec3f(-self->armLength, 0.0f, 0.0f)) - 0.5f * pos_du - 0.5f * pos_dv; + + const vec3f org = + pos_00 + 2.0 * screen.x * pos_du + 2.0 * screen.y * pos_dv; + + float nearClip = self->super.nearClip; + float farClip = infinity; + const varying vec3f direction = normalize(dir); + + clipRay(self->clipPlanes, self->numClipPlanes, org, direction, + nearClip, farClip); + + const float time = Camera_shutterTime(_self, sample.time); + setRay(ray, org, dir, nearClip, farClip, time); + } + else if(screen.x < 0.5f && screen.y >= 0.5f) + { + //front + const vec3f dir = make_vec3f(0.0f, 0.0f, 1.0f); + const vec3f pos_du = make_vec3f(-1.0f, 0.0f, 0.0f) * self->height * self->aspect; + const vec3f pos_dv = make_vec3f(0.0f, 1.0f, 0.0f) * self->height; + const vec3f pos_00 = (self->org + make_vec3f(0.0f, 0.0f, -self->armLength)) - 0.5f * pos_du - 0.5f * pos_dv; + + const vec3f org = + pos_00 + 2.0 * screen.x * pos_du + 2.0 * (screen.y - 0.5f) * pos_dv; + + float nearClip = self->super.nearClip; + float farClip = infinity; + const varying vec3f direction = normalize(dir); + + clipRay(self->clipPlanes, self->numClipPlanes, org, direction, + nearClip, farClip); + + const float time = Camera_shutterTime(_self, sample.time); + setRay(ray, org, dir, nearClip, farClip, time); + } + else if(screen.x >= 0.5f && screen.y < 0.5f) + { + vec3f org = self->org; + + vec3f dir = + self->dir_00 + 2.0f * (screen.x - 0.5f) * self->dir_du + 2.0f * screen.y * self->dir_dv; + + if (self->super.doesDOF) + { + const vec3f llp = uniformSampleDisk(self->scaledAperture, sample.lens); + // transform local lens point to focal plane (dir_XX are prescaled in + // this case) + const vec3f lp = + (llp.x * self->dir_du) + ((llp.y * self->aspect) * self->dir_dv); + org = org + lp; + dir = dir - lp; + } + + float nearClip = self->super.nearClip; + float farClip = infinity; + const varying vec3f direction = normalize(dir); + + clipRay(self->clipPlanes, self->numClipPlanes, org, direction, nearClip, + farClip); + + const float time = Camera_shutterTime(_self, sample.time); + setRay(ray, org, direction, nearClip, farClip, time); + } + else if(screen.x >= 0.5 && screen.y >= 0.5) + { + //top + const vec3f dir = make_vec3f(0.0f, -1.0f, 0.0f); + const vec3f pos_du = make_vec3f(1.0f, 0.0f, 0.0f) * self->height * self->aspect; + const vec3f pos_dv = make_vec3f(0.0f, 0.0f, -1.0f) * self->height; + const vec3f pos_00 = (self->org + make_vec3f(0.0f, self->armLength, 0.0f)) - 0.5f * pos_du - 0.5f * pos_dv; + + const vec3f org = + pos_00 + 2.0 * (screen.x - 0.5f) * pos_du + 2.0 * (screen.y - 0.5f) * pos_dv; + + float nearClip = self->super.nearClip; + float farClip = infinity; + const varying vec3f direction = normalize(dir); + + clipRay(self->clipPlanes, self->numClipPlanes, org, direction, + nearClip, farClip); + + const float time = Camera_shutterTime(_self, sample.time); + setRay(ray, org, dir, nearClip, farClip, time); + } +} + +/*! create a new ispc-side version of a Multiview camera - with given pointer to + the c-class version - and return both class pointer and pointer to internal + data back via the supplied reference parameters. */ +export void* uniform MultiviewCamera_create(void* uniform cppE) +{ + uniform MultiviewCamera* uniform self = uniform new uniform MultiviewCamera; + self->super.cppEquivalent = cppE; + self->super.initRay = MultiviewCamera_initRay; + self->super.doesDOF = false; + return self; +} + +export void MultiviewCamera_set(void* uniform _self, const uniform vec3f& org, const uniform vec3f& dir_00, + const uniform vec3f& dir_du, const uniform vec3f& dir_dv, + const uniform float scaledAperture, const uniform float height, + const uniform float aspect, const uniform float armLength, + const uniform vec4f clipPlanes[], const uniform unsigned int numClipPlanes) +{ + uniform MultiviewCamera* uniform self = + (uniform MultiviewCamera * uniform)_self; + self->org = org; + self->dir_00 = dir_00; + self->dir_du = dir_du; + self->dir_dv = dir_dv; + self->scaledAperture = scaledAperture; + self->super.doesDOF = scaledAperture > 0.f; + self->aspect = aspect; + self->height = height * 10.0f; + self->armLength = armLength; + self->clipPlanes = clipPlanes; + self->numClipPlanes = numClipPlanes; +}