Reflector::Reflector(Object3D* parent, SceneGraph::DrawableGroup3D* group): Object3D(parent), SceneGraph::Drawable3D(*this, group) {
    CubeMapResourceManager& resourceManager = CubeMapResourceManager::instance();

    /* Sphere mesh */
    if(!(_sphere = resourceManager.get<GL::Mesh>("sphere"))) {
        Trade::MeshData3D sphereData = Primitives::uvSphereSolid(16, 32, Primitives::UVSphereTextureCoords::Generate);

        GL::Buffer* buffer = new GL::Buffer;
        buffer->setData(MeshTools::interleave(sphereData.positions(0), sphereData.textureCoords2D(0)), GL::BufferUsage::StaticDraw);

        Containers::Array<char> indexData;
        MeshIndexType indexType;
        UnsignedInt indexStart, indexEnd;
        std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(sphereData.indices());

        GL::Buffer* indexBuffer = new GL::Buffer;
        indexBuffer->setData(indexData, GL::BufferUsage::StaticDraw);

        GL::Mesh* mesh = new GL::Mesh;
        mesh->setPrimitive(sphereData.primitive())
            .setCount(sphereData.indices().size())
            .addVertexBuffer(*buffer, 0, ReflectorShader::Position{}, ReflectorShader::TextureCoords{})
            .setIndexBuffer(*indexBuffer, 0, indexType, indexStart, indexEnd);

        resourceManager.set("sphere-buffer", buffer, ResourceDataState::Final, ResourcePolicy::Resident)
            .set("sphere-index-buffer", indexBuffer, ResourceDataState::Final, ResourcePolicy::Resident)
            .set(_sphere.key(), mesh, ResourceDataState::Final, ResourcePolicy::Resident);
    }

    /* Tarnish texture */
    if(!(_tarnishTexture = resourceManager.get<GL::Texture2D>("tarnish-texture"))) {
        Resource<Trade::AbstractImporter> importer = resourceManager.get<Trade::AbstractImporter>("jpeg-importer");
        Utility::Resource rs("data");
        importer->openData(rs.getRaw("tarnish.jpg"));

        Containers::Optional<Trade::ImageData2D> image = importer->image2D(0);
        CORRADE_INTERNAL_ASSERT(image);
        auto texture = new GL::Texture2D;
        texture->setWrapping(GL::SamplerWrapping::ClampToEdge)
            .setMagnificationFilter(GL::SamplerFilter::Linear)
            .setMinificationFilter(GL::SamplerFilter::Linear, GL::SamplerMipmap::Linear)
            .setStorage(Math::log2(image->size().min())+1, GL::TextureFormat::RGB8, image->size())
            .setSubImage(0, {}, *image)
            .generateMipmap();

        resourceManager.set<GL::Texture2D>(_tarnishTexture.key(), texture, ResourceDataState::Final, ResourcePolicy::Resident);
    }

    /* Reflector shader */
    if(!(_shader = resourceManager.get<GL::AbstractShaderProgram, ReflectorShader>("reflector-shader")))
        resourceManager.set<GL::AbstractShaderProgram>(_shader.key(), new ReflectorShader, ResourceDataState::Final, ResourcePolicy::Resident);

    /* Texture (created in CubeMap class) */
    _texture = resourceManager.get<GL::CubeMapTexture>("texture");
}
void AreaLightsExample::keyPressEvent(KeyEvent& event) {
    /* If an input is focused, pass the events only to the UI */
    if(isTextInputActive() && _ui->focusedInputWidget()) {
        if(!_ui->focusedInputWidget()->handleKeyPress(event)) return;

    /* Movement */
    } else if(event.key() == KeyEvent::Key::W) {
        _cameraDirection = -_view.inverted().backward()*0.01f;
    } else if(event.key() == KeyEvent::Key::S) {
        _cameraDirection = _view.inverted().backward()*0.01f;
    } else if (event.key() == KeyEvent::Key::A) {
        _cameraDirection = Math::cross(_view.inverted().backward(), {0.0f, 1.0f, 0.0})*0.01f;
    } else if (event.key() == KeyEvent::Key::D) {
        _cameraDirection = -Math::cross(_view.inverted().backward(), { 0.0f, 1.0f, 0.0 })*0.01f;

    /* Increase/decrease roughness */
    } else if(event.key() == KeyEvent::Key::R) {
        _roughness = Math::clamp(
            _roughness + 0.01f*(event.modifiers() & KeyEvent::Modifier::Shift ? -1 : 1),
            0.1f, 1.0f);
        _areaLightShader.setRoughness(_roughness);
        _baseUiPlane->roughness.setValue(Utility::formatString("{:.5}", _roughness));

    /* Increase/decrease metalness */
    } else if(event.key() == KeyEvent::Key::M) {
        _metalness = Math::clamp(
            _metalness + 0.01f*(event.modifiers() & KeyEvent::Modifier::Shift ? -1 : 1),
            0.1f, 1.0f);
        _areaLightShader.setMetalness(_metalness);
        _baseUiPlane->metalness.setValue(Utility::formatString("{:.5}", _metalness));

    /* Increase/decrease f0 */
    } else if(event.key() == KeyEvent::Key::F) {
        _f0 = Math::clamp(
            _f0 + 0.01f*(event.modifiers() & KeyEvent::Modifier::Shift ? -1 : 1),
            0.1f, 1.0f);
        _areaLightShader.setF0(_f0);
        _baseUiPlane->f0.setValue(Utility::formatString("{:.5}", _f0));

    /* Reload shader */
    } else if(event.key() == KeyEvent::Key::F5) {
        #ifdef CORRADE_IS_DEBUG_BUILD
        Utility::Resource::overrideGroup("arealights-data", "../src/arealights/resources.conf");
        _areaLightShader = AreaLightShader{};
        #endif

    } else return;

    redraw();
}
void AreaLightsExample::mousePressEvent(MouseEvent& event) {
    if((event.button() == MouseEvent::Button::Left))
        _previousMousePosition = event.position();

    if(!_ui->handlePressEvent(event.position())) return;

    redraw();
}
void AreaLightsExample::mouseMoveEvent(MouseMoveEvent& event) {
    if(_ui->handleMoveEvent(event.position())) {
        /* UI handles it */

    } else if((event.buttons() & MouseMoveEvent::Button::Left)) {
        const Vector2 delta = 3.0f*
            Vector2{event.position() - _previousMousePosition}/Vector2{GL::defaultFramebuffer.viewport().size()};
        _cameraRotation += delta;

        _previousMousePosition = event.position();

    } else return;

    redraw();
}
void AreaLightsExample::textInputEvent(TextInputEvent& event) {
    if(isTextInputActive() && _ui->focusedInputWidget() && _ui->focusedInputWidget()->handleTextInput(event))
        redraw();
}
void AreaLightsExample::mouseReleaseEvent(MouseEvent& event) {
    if(_ui->handleReleaseEvent(event.position()))
        redraw();
}
void AreaLightsExample::drawEvent() {
    GL::defaultFramebuffer.clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth);

    /* Update view matrix */
    _cameraPosition += _cameraDirection;
    _view = Matrix4::rotationX(Rad{_cameraRotation.y()})*
            Matrix4::rotationY(Rad{_cameraRotation.x()})*
            Matrix4::translation(-_cameraPosition);

    /* Draw light on the floor. Cheat a bit and just add everything together,
       enabling depth test for the first only. Will work as long as the
       background is black. */
    GL::Renderer::enable(GL::Renderer::Feature::Blending);
    GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::One, GL::Renderer::BlendFunction::One);
    _areaLightShader.bindTextures(_ltcMat, _ltcAmp);
    for(std::size_t i: {0, 1, 2}) {
        Vector3 quadPoints[4];
        for(std::size_t p: {0, 1, 2, 3})
            quadPoints[p] = _lightTransform[i].transformPoint(LightVertices[p].position);

        _areaLightShader
            .setTransformationMatrix(_transformation)
            .setProjectionMatrix(_projection)
            .setViewMatrix(_view)
            .setNormalMatrix(_transformation.rotationScaling())
            .setViewPosition(_view.invertedRigid().translation())
            .setLightQuad(quadPoints)
            .setBaseColor(_lightColor[i])
            .setLightIntensity(_lightIntensity[i])
            .setTwoSided(_lightTwoSided[i]);

        if(i == 0)
            GL::Renderer::enable(GL::Renderer::Feature::DepthTest);

        _plane.draw(_areaLightShader);

        if(i == 0)
            GL::Renderer::disable(GL::Renderer::Feature::DepthTest);
    }
    GL::Renderer::disable(GL::Renderer::Feature::Blending);

    /* Draw light visualization, this time with depth test enabled for all.
       Draw twice for two-sided lights. */
    GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
    for(std::size_t i: {0, 1, 2}) {
        _flatShader.setColor(_lightColor[i]*_lightIntensity[i]*1.25f)
            .setTransformationProjectionMatrix(_projection*_view*_lightTransform[i]);
        _plane.draw(_flatShader);

        if(_lightTwoSided[i]) {
            _flatShader.setTransformationProjectionMatrix(_projection*_view*_lightTransform[i]*Matrix4::scaling(Vector3::xScale(-1.0f)));
            _plane.draw(_flatShader);
        }
    }
    GL::Renderer::disable(GL::Renderer::Feature::DepthTest);

    /* Draw the UI */
    GL::Renderer::enable(GL::Renderer::Feature::Blending);
    GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::One, GL::Renderer::BlendFunction::OneMinusSourceAlpha);
    _ui->draw();
    GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::One, GL::Renderer::BlendFunction::One);
    GL::Renderer::disable(GL::Renderer::Feature::Blending);

    /* Redraw only if moving somewhere */
    swapBuffers();
    if(!_cameraDirection.isZero()) redraw();
}
AreaLightsExample::AreaLightsExample(const Arguments& arguments): Platform::Application{arguments, NoCreate} {
    /* Try to create multisampled context, but be nice and fall back if not
       available. Enable only 2x MSAA if we have enough DPI. */
    {
        const Vector2 dpiScaling = this->dpiScaling({});
        Configuration conf;
        conf.setTitle("Magnum Area Lights Example")
            .setSize(conf.size(), dpiScaling);
        GLConfiguration glConf;
        glConf.setSampleCount(dpiScaling.max() < 2.0f ? 8 : 2);
        if(!tryCreate(conf, glConf))
            create(conf, glConf.setSampleCount(0));
    }

    /* Make it all DARK, eanble face culling so one-sided lights are properly
       visualized */
    GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
    GL::Renderer::setClearColor(0x000000_rgbf);

    /* Setup the plane mesh, which will be used for both the floor and light
       visualization */
    _vertices = GL::Buffer{};
    _vertices.setData(LightVertices, GL::BufferUsage::StaticDraw);
    _plane = GL::Mesh{};
    _plane.setPrimitive(GL::MeshPrimitive::TriangleFan)
        .addVertexBuffer(_vertices, 0, Shaders::Generic3D::Position{}, Shaders::Generic3D::Normal{})
        .setCount(Containers::arraySize(LightVertices));

    /* Setup project and floor plane tranformation matrix */
    _projection = Matrix4::perspectiveProjection(60.0_degf, 4.0f/3.0f, 0.1f, 50.0f);
    _transformation = Matrix4::rotationX(-90.0_degf)*Matrix4::scaling(Vector3{25.0f});

    /* Load LTC matrix and BRDF textures */
    PluginManager::Manager<Trade::AbstractImporter> manager;
    Containers::Pointer<Trade::AbstractImporter> importer = manager.loadAndInstantiate("DdsImporter");
    if(!importer) std::exit(1);

    const Utility::Resource rs{"arealights-data"};
    if(!importer->openData(rs.getRaw("ltc_amp.dds")))
        std::exit(2);

    /* Set texture data and parameters */
    Containers::Optional<Trade::ImageData2D> image = importer->image2D(0);
    CORRADE_INTERNAL_ASSERT(image);
    _ltcAmp = GL::Texture2D{};
    _ltcAmp.setWrapping(GL::SamplerWrapping::ClampToEdge)
        .setMagnificationFilter(GL::SamplerFilter::Linear)
        .setMinificationFilter(GL::SamplerFilter::Linear)
        .setStorage(1, GL::TextureFormat::RG32F, image->size())
        .setSubImage(0, {}, *image);

    if(!importer->openData(rs.getRaw("ltc_mat.dds")))
        std::exit(2);

    /* Set texture data and parameters */
    image = importer->image2D(0);
    CORRADE_INTERNAL_ASSERT(image);
    _ltcMat = GL::Texture2D{};
    _ltcMat.setWrapping(GL::SamplerWrapping::ClampToEdge)
        .setMagnificationFilter(GL::SamplerFilter::Linear)
        .setMinificationFilter(GL::SamplerFilter::Linear)
        .setStorage(1, GL::TextureFormat::RGBA32F, image->size())
        .setSubImage(0, {}, *image);

    /* Compile shaders */
    _areaLightShader = AreaLightShader{};
    _flatShader = Shaders::Flat3D{};

    /* Create the UI */
    _ui.emplace(Vector2{windowSize()}/dpiScaling(), windowSize(), framebufferSize(), Ui::mcssDarkStyleConfiguration(), "ƒ₀");
    Interconnect::connect(*_ui, &Ui::UserInterface::inputWidgetFocused, *this, &AreaLightsExample::startTextInput);
    Interconnect::connect(*_ui, &Ui::UserInterface::inputWidgetBlurred, *this, &AreaLightsExample::stopTextInput);

    /* Base UI plane */
    _baseUiPlane.emplace(*_ui);
    Interconnect::connect(_baseUiPlane->metalness, &Ui::Input::valueChanged, *this, &AreaLightsExample::enableApplyButton);
    Interconnect::connect(_baseUiPlane->roughness, &Ui::Input::valueChanged, *this, &AreaLightsExample::enableApplyButton);
    Interconnect::connect(_baseUiPlane->f0, &Ui::Input::valueChanged, *this, &AreaLightsExample::enableApplyButton);
    Interconnect::connect(_baseUiPlane->apply, &Ui::Button::tapped, *this, &AreaLightsExample::apply);
    Interconnect::connect(_baseUiPlane->reset, &Ui::Button::tapped, *this, &AreaLightsExample::reset);

    /* Apply the default values */
    apply();
}
Exemple #9
0
CubeMap::CubeMap(const std::string& prefix, Object3D* parent, SceneGraph::DrawableGroup3D* group): Object3D(parent), SceneGraph::Drawable3D(*this, group) {
    CubeMapResourceManager& resourceManager = CubeMapResourceManager::instance();

    /* Cube mesh */
    if(!(_cube = resourceManager.get<GL::Mesh>("cube"))) {
        Trade::MeshData3D cubeData = Primitives::cubeSolid();
        MeshTools::flipFaceWinding(cubeData.indices());

        GL::Buffer* buffer = new GL::Buffer;
        buffer->setData(MeshTools::interleave(cubeData.positions(0)), GL::BufferUsage::StaticDraw);

        Containers::Array<char> indexData;
        MeshIndexType indexType;
        UnsignedInt indexStart, indexEnd;
        std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(cubeData.indices());

        GL::Buffer* indexBuffer = new GL::Buffer;
        indexBuffer->setData(indexData, GL::BufferUsage::StaticDraw);

        GL::Mesh* mesh = new GL::Mesh;
        mesh->setPrimitive(cubeData.primitive())
            .setCount(cubeData.indices().size())
            .addVertexBuffer(*buffer, 0, CubeMapShader::Position{})
            .setIndexBuffer(*indexBuffer, 0, indexType, indexStart, indexEnd);

        resourceManager.set("cube-buffer", buffer, ResourceDataState::Final, ResourcePolicy::Resident)
            .set("cube-index-buffer", indexBuffer, ResourceDataState::Final, ResourcePolicy::Resident)
            .set(_cube.key(), mesh, ResourceDataState::Final, ResourcePolicy::Resident);
    }

    /* Cube map texture */
    if(!(_texture = resourceManager.get<GL::CubeMapTexture>("texture"))) {
        GL::CubeMapTexture* cubeMap = new GL::CubeMapTexture;

        cubeMap->setWrapping(GL::SamplerWrapping::ClampToEdge)
            .setMagnificationFilter(GL::SamplerFilter::Linear)
            .setMinificationFilter(GL::SamplerFilter::Linear, GL::SamplerMipmap::Linear);

        Resource<Trade::AbstractImporter> importer = resourceManager.get<Trade::AbstractImporter>("jpeg-importer");

        /* Configure texture storage using size of first image */
        importer->openFile(prefix + "+x.jpg");
        Containers::Optional<Trade::ImageData2D> image = importer->image2D(0);
        CORRADE_INTERNAL_ASSERT(image);
        Vector2i size = image->size();
        cubeMap->setStorage(Math::log2(size.min())+1, GL::TextureFormat::RGB8, size)
            .setSubImage(GL::CubeMapCoordinate::PositiveX, 0, {}, *image);

        importer->openFile(prefix + "-x.jpg");
        CORRADE_INTERNAL_ASSERT_OUTPUT(image = importer->image2D(0));
        cubeMap->setSubImage(GL::CubeMapCoordinate::NegativeX, 0, {}, *image);

        importer->openFile(prefix + "+y.jpg");
        CORRADE_INTERNAL_ASSERT_OUTPUT(image = importer->image2D(0));
        cubeMap->setSubImage(GL::CubeMapCoordinate::PositiveY, 0, {}, *image);

        importer->openFile(prefix + "-y.jpg");
        CORRADE_INTERNAL_ASSERT_OUTPUT(image = importer->image2D(0));
        cubeMap->setSubImage(GL::CubeMapCoordinate::NegativeY, 0, {}, *image);

        importer->openFile(prefix + "+z.jpg");
        CORRADE_INTERNAL_ASSERT_OUTPUT(image = importer->image2D(0));
        cubeMap->setSubImage(GL::CubeMapCoordinate::PositiveZ, 0, {}, *image);

        importer->openFile(prefix + "-z.jpg");
        CORRADE_INTERNAL_ASSERT_OUTPUT(image = importer->image2D(0));
        cubeMap->setSubImage(GL::CubeMapCoordinate::NegativeZ, 0, {}, *image);

        cubeMap->generateMipmap();

        resourceManager.set(_texture.key(), cubeMap, ResourceDataState::Final, ResourcePolicy::Manual);
    }

    /* Shader */
    if(!(_shader = resourceManager.get<GL::AbstractShaderProgram, CubeMapShader>("shader")))
        resourceManager.set<GL::AbstractShaderProgram>(_shader.key(), new CubeMapShader, ResourceDataState::Final, ResourcePolicy::Manual);
}