diff --git a/simgear/scene/model/SGLight.cxx b/simgear/scene/model/SGLight.cxx index ab332750..269829e1 100644 --- a/simgear/scene/model/SGLight.cxx +++ b/simgear/scene/model/SGLight.cxx @@ -73,32 +73,39 @@ SGLight::appendLight(const SGPropertyNode *configNode, light->setSpotExponent(configNode->getFloatValue("spot-exponent")); light->setSpotCutoff(configNode->getFloatValue("spot-cutoff")); - osg::Group *group = 0; - if ((p = configNode->getNode("offsets")) == NULL) { - group = new osg::Group; - } else { - // Set up the alignment node ("stolen" from animation.cxx) - // XXX Order of rotations is probably not correct. - osg::MatrixTransform *align = new osg::MatrixTransform; - osg::Matrix res_matrix; - res_matrix.makeRotate( - p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 1, 0), - p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(1, 0, 0), - p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 0, 1)); + osg::MatrixTransform *align = new osg::MatrixTransform; + align->addChild(light); - osg::Matrix tmat; - tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0), - configNode->getFloatValue("offsets/y-m", 0.0), - configNode->getFloatValue("offsets/z-m", 0.0)); + osg::Matrix t; + osg::Vec3 pos(configNode->getFloatValue("position/x-m"), + configNode->getFloatValue("position/y-m"), + configNode->getFloatValue("position/z-m")); + t.makeTranslate(pos); - align->setMatrix(res_matrix * tmat); - group = align; + osg::Matrix r; + if (const SGPropertyNode *dirNode = configNode->getNode("direction")) { + if (dirNode->hasValue("pitch-deg")) { + r.makeRotate( + dirNode->getFloatValue("pitch-deg")*SG_DEGREES_TO_RADIANS, + osg::Vec3(0, 1, 0), + dirNode->getFloatValue("roll-deg")*SG_DEGREES_TO_RADIANS, + osg::Vec3(1, 0, 0), + dirNode->getFloatValue("heading-deg")*SG_DEGREES_TO_RADIANS, + osg::Vec3(0, 0, 1)); + } else if (dirNode->hasValue("lookat-x-m")) { + osg::Vec3 lookAt(dirNode->getFloatValue("lookat-x-m"), + dirNode->getFloatValue("lookat-y-m"), + dirNode->getFloatValue("lookat-z-m")); + osg::Vec3 dir = lookAt - pos; + r.makeRotate(osg::Vec3(0, 0, -1), dir); + } else { + r.makeRotate(osg::Vec3(0, 0, -1), + osg::Vec3(dirNode->getFloatValue("x"), + dirNode->getFloatValue("y"), + dirNode->getFloatValue("z"))); + } } - - group->addChild(light); + align->setMatrix(r * t); osg::Shape *debug_shape; if (light->getType() == SGLight::Type::POINT) { @@ -125,14 +132,14 @@ SGLight::appendLight(const SGPropertyNode *configNode, debug_switch->addChild(debug_geode); simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)-> addChangeListener(new SGLightDebugListener(debug_switch), true); - group->addChild(debug_switch); + align->addChild(debug_switch); if ((p = configNode->getNode("name")) != NULL) - group->setName(p->getStringValue()); + align->setName(p->getStringValue()); else - group->setName("light"); + align->setName("light"); - return group; + return align; } SGLight::SGLight() : diff --git a/simgear/scene/viewer/ClusteredShading.cxx b/simgear/scene/viewer/ClusteredShading.cxx index e42883a2..ba521a4d 100644 --- a/simgear/scene/viewer/ClusteredShading.cxx +++ b/simgear/scene/viewer/ClusteredShading.cxx @@ -27,6 +27,7 @@ #include +#include #include namespace simgear { @@ -40,7 +41,7 @@ const int MAX_SPOTLIGHTS = 256; // It must be a multiple of the size of a vec4 as per the std140 layout rules. // See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt const int POINTLIGHT_BLOCK_SIZE = 20; -const int SPOTLIGHT_BLOCK_SIZE = 8; +const int SPOTLIGHT_BLOCK_SIZE = 28; ClusteredShading::ClusteredShading(osg::Camera *camera, const SGPropertyNode *config) : @@ -180,12 +181,38 @@ ClusteredShading::update(const SGLightList &light_list) PointlightBound point; point.light = light; point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) * - osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * - _camera->getViewMatrix(); + // The parenthesis are very important: if the vector is + // multiplied by the local to world matrix first we'll have + // precision issues + (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix()); point.range = light->getRange(); + _point_bounds.push_back(point); } else if (light->getType() == SGLight::Type::SPOT) { + SpotlightBound spot; + spot.light = light; + spot.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) * + (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix()); + spot.direction = osg::Vec4f(0.0f, 0.0f, -1.0f, 0.0f) * + (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix()); + float range = light->getRange(); + float angle = light->getSpotCutoff() * SG_DEGREES_TO_RADIANS; + spot.cos_cutoff = cos(angle); + if(angle > SGD_PI_4) { + spot.bounding_sphere.radius = range * tan(angle); + } else { + spot.bounding_sphere.radius = + range * 0.5f / pow(spot.cos_cutoff, 2.0f); + } + + spot.bounding_sphere.center = + spot.position + spot.direction * spot.bounding_sphere.radius; + + _spot_bounds.push_back(spot); } } if (_point_bounds.size() > MAX_POINTLIGHTS || @@ -265,8 +292,9 @@ ClusteredShading::update(const SGLightList &light_list) _light_grid->dirty(); _light_indices->dirty(); - // Upload pointlight data + // Upload pointlight and spotlight data writePointlightData(); + writeSpotlightData(); } void @@ -301,6 +329,7 @@ ClusteredShading::assignLightsToSlice(int slice) GLuint local_point_count = 0; GLuint local_spot_count = 0; + // Test point lights for (GLushort point_iterator = 0; point_iterator < _point_bounds.size(); ++point_iterator) { @@ -328,25 +357,40 @@ ClusteredShading::assignLightsToSlice(int slice) } } + // Test spot lights + for (GLushort spot_iterator = 0; + spot_iterator < _spot_bounds.size(); + ++spot_iterator) { + SpotlightBound spot = _spot_bounds[spot_iterator]; + + // Perform frustum-sphere collision tests + float distance = 0.0f; + for (int j = 0; j < 6; j++) { + distance = subfrustum.plane[j] * spot.bounding_sphere.center + + spot.bounding_sphere.radius; + if (distance <= 0.0f) + break; + } + + if (distance > 0.0f) { + // Update light index list + indices[_global_light_count] = spot_iterator; + ++local_spot_count; + ++_global_light_count; // Atomic increment + } + + if (_global_light_count >= MAX_LIGHT_INDICES) { + throw sg_range_exception( + "Clustered shading light index count is over the hardcoded limit (" + + std::to_string(MAX_LIGHT_INDICES) + ")"); + } + } + // Update light grid grid[(z_offset + i) * 3 + 0] = start_offset; grid[(z_offset + i) * 3 + 1] = local_point_count; grid[(z_offset + i) * 3 + 2] = local_spot_count; } - - // for (int y = 0; y < _n_vtiles; ++y) { - // for (int x = 0; x < _n_htiles; ++x) { - // std::cout << grid[(y * _n_htiles + x) * 3 + 0] << "," - // << grid[(y * _n_htiles + x) * 3 + 1] << " "; - // } - // std::cout << std::endl; - // } - // std::cout << "\n\n"; - - // for (int i = 0; i < n_vtiles * n_htiles; ++i) { - // std::cout << indices[i] << " "; - // } - // std::cout << "\n"; } void @@ -385,6 +429,51 @@ ClusteredShading::writePointlightData() _pointlight_data->dirty(); } +void +ClusteredShading::writeSpotlightData() +{ + GLfloat *data = reinterpret_cast(&(*_spotlight_data)[0]); + + for (const auto &spot : _spot_bounds) { + // vec4 position + *data++ = spot.position.x(); + *data++ = spot.position.y(); + *data++ = spot.position.z(); + *data++ = 1.0f; + // vec4 direction + *data++ = spot.direction.x(); + *data++ = spot.direction.y(); + *data++ = spot.direction.z(); + *data++ = 0.0f; + // vec4 ambient + *data++ = spot.light->getAmbient().x(); + *data++ = spot.light->getAmbient().y(); + *data++ = spot.light->getAmbient().z(); + *data++ = spot.light->getAmbient().w(); + // vec4 diffuse + *data++ = spot.light->getDiffuse().x(); + *data++ = spot.light->getDiffuse().y(); + *data++ = spot.light->getDiffuse().z(); + *data++ = spot.light->getDiffuse().w(); + // vec4 specular + *data++ = spot.light->getSpecular().x(); + *data++ = spot.light->getSpecular().y(); + *data++ = spot.light->getSpecular().z(); + *data++ = spot.light->getSpecular().w(); + // vec4 attenuation (x = constant, y = linear, z = quadratic, w = range) + *data++ = spot.light->getConstantAttenuation(); + *data++ = spot.light->getLinearAttenuation(); + *data++ = spot.light->getQuadraticAttenuation(); + *data++ = spot.light->getRange(); + // float cos_cutoff + *data++ = spot.cos_cutoff; + // float exponent + *data++ = spot.light->getSpotExponent(); + // Needs 2N padding (8 bytes) + } + _spotlight_data->dirty(); +} + float ClusteredShading::getDepthForSlice(int slice) const { diff --git a/simgear/scene/viewer/ClusteredShading.hxx b/simgear/scene/viewer/ClusteredShading.hxx index 4843a102..fed11f1f 100644 --- a/simgear/scene/viewer/ClusteredShading.hxx +++ b/simgear/scene/viewer/ClusteredShading.hxx @@ -42,19 +42,25 @@ protected: }; struct PointlightBound { - SGLight *light; + SGLight *light = nullptr; osg::Vec4f position; - float range; + float range = 0.0f; }; struct SpotlightBound { - SGLight *light; + SGLight *light = nullptr; osg::Vec4f position; - float range; + osg::Vec4f direction; + float cos_cutoff = 0.0f; + struct { + osg::Vec4f center; + float radius = 0.0f; + } bounding_sphere; }; void threadFunc(int thread_id); void assignLightsToSlice(int slice); void writePointlightData(); + void writeSpotlightData(); float getDepthForSlice(int slice) const; osg::observer_ptr _camera; diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 2a67a6d4..2187a494 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -567,8 +567,7 @@ struct ShadowMapPassBuilder : public PassBuilder { osg::Camera *camera = pass->camera; camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); - camera->setCullingMode(camera->getCullingMode() & - ~osg::CullSettings::SMALL_FEATURE_CULLING); + camera->setCullingMode(osg::CullSettings::ENABLE_ALL_CULLING); //camera->setComputeNearFarMode( // osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);