From 9f862dcc0e614899eb7249f2961174bd36c92c3c Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Mon, 27 Jan 2020 09:28:25 +0000 Subject: [PATCH] Fix directional lights for AMD Some AMD drivers do not like triangles for point sprites, which breaks directional lights. This works around this by allowing users to set /rendering/triangle-directional-lights=false which falls back to the non-directional implementation of a point. --- simgear/scene/tgdb/pt_lights.cxx | 84 ++++++++++++++++---------- simgear/scene/tgdb/pt_lights.hxx | 6 +- simgear/scene/util/SGSceneFeatures.cxx | 8 +-- simgear/scene/util/SGSceneFeatures.hxx | 17 ++++++ 4 files changed, 77 insertions(+), 38 deletions(-) diff --git a/simgear/scene/tgdb/pt_lights.cxx b/simgear/scene/tgdb/pt_lights.cxx index 11ba4539..cad28567 100644 --- a/simgear/scene/tgdb/pt_lights.cxx +++ b/simgear/scene/tgdb/pt_lights.cxx @@ -153,41 +153,49 @@ SGLightFactory::getLightDrawable(const SGLightBin::Light& light) } osg::Drawable* -SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light) +SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light, bool useTriangles) { osg::Vec3Array* vertices = new osg::Vec3Array; osg::Vec4Array* colors = new osg::Vec4Array; - SGVec4f visibleColor(light.color); - SGVec4f invisibleColor(visibleColor[0], visibleColor[1], - visibleColor[2], 0); - SGVec3f normal = normalize(light.normal); - SGVec3f perp1 = perpendicular(normal); - SGVec3f perp2 = cross(normal, perp1); - SGVec3f position = light.position; - vertices->push_back(toOsg(position)); - vertices->push_back(toOsg(position + perp1)); - vertices->push_back(toOsg(position + perp2)); - colors->push_back(toOsg(visibleColor)); - colors->push_back(toOsg(invisibleColor)); - colors->push_back(toOsg(invisibleColor)); + if (useTriangles) { + SGVec4f visibleColor(light.color); + SGVec4f invisibleColor(visibleColor[0], visibleColor[1], + visibleColor[2], 0); + SGVec3f normal = normalize(light.normal); + SGVec3f perp1 = perpendicular(normal); + SGVec3f perp2 = cross(normal, perp1); + SGVec3f position = light.position; + vertices->push_back(toOsg(position)); + vertices->push_back(toOsg(position + perp1)); + vertices->push_back(toOsg(position + perp2)); + colors->push_back(toOsg(visibleColor)); + colors->push_back(toOsg(invisibleColor)); + colors->push_back(toOsg(invisibleColor)); - osg::Geometry* geometry = new osg::Geometry; - geometry->setDataVariance(osg::Object::STATIC); - geometry->setVertexArray(vertices); - geometry->setNormalBinding(osg::Geometry::BIND_OFF); - geometry->setColorArray(colors); - geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + osg::Geometry* geometry = new osg::Geometry; + geometry->setDataVariance(osg::Object::STATIC); + geometry->setVertexArray(vertices); + geometry->setNormalBinding(osg::Geometry::BIND_OFF); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); - // Enlarge the bounding box to avoid such light nodes being victim to - // small feature culling. - geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1)); + // Enlarge the bounding box to avoid such light nodes being victim to + // small feature culling. + geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1)); - osg::DrawArrays* drawArrays; - drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, - 0, vertices->size()); - geometry->addPrimitiveSet(drawArrays); - return geometry; + osg::DrawArrays* drawArrays; + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, + 0, vertices->size()); + geometry->addPrimitiveSet(drawArrays); + return geometry; + } else { + // Workaround for driver issue where point sprites cannot be triangles, + // (which we use to provide a sensible normal). So fall back to a simple + // non-directional point sprite. + const SGLightBin::Light nonDirectionalLight = SGLightBin::Light(light.position, light.color); + return getLightDrawable(nonDirectionalLight); + } } namespace @@ -367,6 +375,9 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader if (lights.getNumLights() <= 0) return 0; + static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance(); + bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights(); + // generate a repeatable random seed sg_srandom(unsigned(lights.getLight(0).position[0])); float flashTime = 0.065 + 0.003 * sg_random(); @@ -377,7 +388,7 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader for (int i = lights.getNumLights() - 1; 0 <= i; --i) { EffectGeode* egeode = new EffectGeode; egeode->setEffect(effect); - egeode->addDrawable(getLightDrawable(lights.getLight(i))); + egeode->addDrawable(getLightDrawable(lights.getLight(i), useTriangles)); sequence->addChild(egeode, flashTime); } sequence->addChild(new osg::Group, 1.9 + (0.1 * sg_random()) - (lights.getNumLights() * flashTime)); @@ -394,6 +405,9 @@ SGLightFactory::getReil(const SGDirectionalLightBin& lights, const SGReaderWrite if (lights.getNumLights() <= 0) return 0; + static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance(); + bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights(); + // generate a repeatable random seed sg_srandom(unsigned(lights.getLight(0).position[0])); float flashTime = 0.065 + 0.003 * sg_random(); @@ -405,7 +419,7 @@ SGLightFactory::getReil(const SGDirectionalLightBin& lights, const SGReaderWrite egeode->setEffect(effect); for (int i = lights.getNumLights() - 1; 0 <= i; --i) { - egeode->addDrawable(getLightDrawable(lights.getLight(i))); + egeode->addDrawable(getLightDrawable(lights.getLight(i), useTriangles)); } sequence->addChild(egeode, flashTime); sequence->addChild(new osg::Group, 1.9 + 0.1 * sg_random() - flashTime); @@ -463,6 +477,9 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader if (lights.getNumLights() < 2) return 0; + static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance(); + bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights(); + sg_srandom(unsigned(lights.getLight(0).position[0])); float flashTime = 0.9 + 0.2 * sg_random(); osg::Sequence* sequence = new osg::Sequence; @@ -476,7 +493,7 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader EffectGeode* egeode = new EffectGeode; egeode->setEffect(effect); for (unsigned int j = 0; j < lights.getNumLights(); ++j) { - egeode->addDrawable(getLightDrawable(lights.getLight(j))); + egeode->addDrawable(getLightDrawable(lights.getLight(j), useTriangles)); } sequence->addChild(egeode, (i==4) ? flashTime : 0.1); } @@ -494,6 +511,9 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit if (lights.getNumLights() < 2) return 0; + static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance(); + bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights(); + // generate a repeatable random seed sg_srandom(unsigned(lights.getLight(0).position[0])); float flashTime = 0.9 + 0.2 * sg_random(); @@ -504,7 +524,7 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit for (unsigned int i = 0; i < lights.getNumLights(); ++i) { EffectGeode* egeode = new EffectGeode; egeode->setEffect(effect); - egeode->addDrawable(getLightDrawable(lights.getLight(i))); + egeode->addDrawable(getLightDrawable(lights.getLight(i), useTriangles)); sequence->addChild(egeode, flashTime); } sequence->setInterval(osg::Sequence::LOOP, 0, -1); diff --git a/simgear/scene/tgdb/pt_lights.hxx b/simgear/scene/tgdb/pt_lights.hxx index 005aea1d..0b8d15d5 100644 --- a/simgear/scene/tgdb/pt_lights.hxx +++ b/simgear/scene/tgdb/pt_lights.hxx @@ -55,10 +55,12 @@ class Effect; // appropriate extensions are available.) inline void SGConfigureDirectionalLights( bool use_point_sprites, - bool distance_attenuation ) { + bool distance_attenuation, + bool use_triangles ) { static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance(); sceneFeatures->setEnablePointSpriteLights(use_point_sprites); sceneFeatures->setEnableDistanceAttenuationLights(distance_attenuation); + sceneFeatures->setEnableTriangleDirectionalLights(use_triangles); } class SGLightFactory { @@ -68,7 +70,7 @@ public: getLightDrawable(const SGLightBin::Light& light); static osg::Drawable* - getLightDrawable(const SGDirectionalLightBin::Light& light); + getLightDrawable(const SGDirectionalLightBin::Light& light, bool useTriangles); /** * Return a drawable for a very simple point light that isn't diff --git a/simgear/scene/util/SGSceneFeatures.cxx b/simgear/scene/util/SGSceneFeatures.cxx index 48aecf22..f17b52ac 100644 --- a/simgear/scene/util/SGSceneFeatures.cxx +++ b/simgear/scene/util/SGSceneFeatures.cxx @@ -1,6 +1,6 @@ /* -*-c++-*- * - * Copyright (C) 2006-2007 Mathias Froehlich + * Copyright (C) 2006-2007 Mathias Froehlich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,6 +46,7 @@ SGSceneFeatures::SGSceneFeatures() : _TextureCacheActive(false), _shaderLights(true), _pointSpriteLights(true), + _triangleDirectionalLights(true), _distanceAttenuationLights(true), _textureFilter(1) { @@ -108,7 +109,7 @@ SGSceneFeatures::getHaveFragmentPrograms(unsigned contextId) const return false; if (!fpe->isFragmentProgramSupported()) return false; - + return true; #else const osg::GLExtensions* ex = osg::GLExtensions::Get(contextId, true); @@ -126,7 +127,7 @@ SGSceneFeatures::getHaveVertexPrograms(unsigned contextId) const return false; if (!vpe->isVertexProgramSupported()) return false; - + return true; #else const osg::GLExtensions* ex = osg::GLExtensions::Get(contextId, true); @@ -158,4 +159,3 @@ SGSceneFeatures::getHavePointParameters(unsigned contextId) const return ex && ex->isPointParametersSupported; #endif } - diff --git a/simgear/scene/util/SGSceneFeatures.hxx b/simgear/scene/util/SGSceneFeatures.hxx index 5b9f7f7c..cb9453d5 100644 --- a/simgear/scene/util/SGSceneFeatures.hxx +++ b/simgear/scene/util/SGSceneFeatures.hxx @@ -75,6 +75,21 @@ public: return getHavePointSprites(contextId); } + void setEnableTriangleDirectionalLights(bool enable) + { + _triangleDirectionalLights = enable; + } + bool getEnableTriangleDirectionalLights() const + { + return _triangleDirectionalLights; + } + bool getEnableTriangleDirectionalLights(unsigned contextId) const + { + if (!_triangleDirectionalLights) + return false; + return getHaveTriangleDirectionals(contextId); + } + void setEnableDistanceAttenuationLights(bool enable) { _distanceAttenuationLights = enable; @@ -108,6 +123,7 @@ public: protected: bool getHavePointSprites(unsigned contextId) const; + bool getHaveTriangleDirectionals(unsigned contextId) const; bool getHaveFragmentPrograms(unsigned contextId) const; bool getHaveVertexPrograms(unsigned contextId) const; bool getHaveShaderPrograms(unsigned contextId) const; @@ -126,6 +142,7 @@ private: bool _TextureCacheActive; bool _shaderLights; bool _pointSpriteLights; + bool _triangleDirectionalLights; bool _distanceAttenuationLights; int _textureFilter; };