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(); }
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); }