LCOV - code coverage report
Current view: top level - src/osgview - GUIOSGBuilder.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 84.8 % 408 346
Test Date: 2024-11-21 15:56:26 Functions: 81.8 % 11 9

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    GUIOSGBuilder.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Michael Behrisch
      17              : /// @author  Mirko Barthauer
      18              : /// @date    19.01.2012
      19              : ///
      20              : // Builds OSG nodes from microsim objects
      21              : /****************************************************************************/
      22              : #include <config.h>
      23              : 
      24              : #ifdef HAVE_OSG
      25              : 
      26              : #include <guisim/GUIEdge.h>
      27              : #include <guisim/GUIJunctionWrapper.h>
      28              : #include <guisim/GUILane.h>
      29              : #include <guisim/GUINet.h>
      30              : #include <microsim/MSEdge.h>
      31              : #include <microsim/MSEdgeControl.h>
      32              : #include <microsim/MSJunction.h>
      33              : #include <microsim/MSJunctionControl.h>
      34              : #include <microsim/MSLane.h>
      35              : #include <microsim/MSNet.h>
      36              : #include <microsim/MSVehicleType.h>
      37              : #include <microsim/traffic_lights/MSTLLogicControl.h>
      38              : #include <microsim/traffic_lights/MSTrafficLightLogic.h>
      39              : #include <utils/common/MsgHandler.h>
      40              : #include <utils/common/SUMOVehicleClass.h>
      41              : #include <utils/geom/GeomHelper.h>
      42              : #include <utils/gui/windows/GUISUMOAbstractView.h>
      43              : 
      44              : #include "GUIOSGView.h"
      45              : #include "GUIOSGBuilder.h"
      46              : 
      47              : //#define DEBUG_TESSEL
      48              : 
      49              : // ===========================================================================
      50              : // static member variables
      51              : // ===========================================================================
      52              : 
      53              : std::map<std::string, osg::ref_ptr<osg::Node> > GUIOSGBuilder::myCars;
      54              : 
      55              : // ===========================================================================
      56              : // member method definitions
      57              : // ===========================================================================
      58              : 
      59              : osg::Group*
      60          411 : GUIOSGBuilder::buildOSGScene(osg::Node* const tlg, osg::Node* const tly, osg::Node* const tlr, osg::Node* const tlu, osg::Node* const pole) {
      61          411 :     osgUtil::Tessellator tesselator;
      62          411 :     osg::Group* root = new osg::Group();
      63          411 :     GUINet* net = static_cast<GUINet*>(MSNet::getInstance());
      64              :     // build edges
      65        11701 :     for (const MSEdge* e : net->getEdgeControl().getEdges()) {
      66        11290 :         if (!e->isInternal()) {
      67         4041 :             buildOSGEdgeGeometry(*e, *root, tesselator);
      68              :         }
      69              :     }
      70              :     // build junctions
      71         4870 :     for (int index = 0; index < (int)net->myJunctionWrapper.size(); ++index) {
      72         4459 :         buildOSGJunctionGeometry(*net->myJunctionWrapper[index], *root, tesselator);
      73              :     }
      74              :     // build traffic lights
      75              :     GUISUMOAbstractView::Decal d;
      76          411 :     const std::vector<std::string> tlids = net->getTLSControl().getAllTLIds();
      77          516 :     for (std::vector<std::string>::const_iterator i = tlids.begin(); i != tlids.end(); ++i) {
      78          105 :         MSTLLogicControl::TLSLogicVariants& vars = net->getTLSControl().get(*i);
      79          105 :         buildTrafficLightDetails(vars, tlg, tly, tlr, tlu, pole, *root);
      80              : 
      81          105 :         const MSTrafficLightLogic::LaneVectorVector& lanes = vars.getActive()->getLaneVectors();
      82              :         const MSLane* lastLane = 0;
      83              :         int idx = 0;
      84         1646 :         for (MSTrafficLightLogic::LaneVectorVector::const_iterator j = lanes.begin(); j != lanes.end(); ++j, ++idx) {
      85         1541 :             if ((*j).size() == 0) {
      86            0 :                 continue;
      87              :             }
      88         1541 :             const MSLane* const lane = (*j)[0];
      89         1541 :             const Position pos = lane->getShape().back();
      90         1541 :             const double angle = osg::DegreesToRadians(lane->getShape().rotationDegreeAtOffset(-1.) + 90.);
      91         1541 :             d.centerZ = pos.z() + 4.;
      92         1541 :             if (lane == lastLane) {
      93          865 :                 d.centerX += 1.2 * sin(angle);
      94          865 :                 d.centerY += 1.2 * cos(angle);
      95              :             } else {
      96          676 :                 d.centerX = pos.x() - 1.5 * sin(angle);
      97          676 :                 d.centerY = pos.y() - 1.5 * cos(angle);
      98              :             }
      99         1541 :             osg::PositionAttitudeTransform* tlNode = getTrafficLight(d, vars, vars.getActive()->getLinksAt(idx)[0], nullptr, nullptr, nullptr, nullptr, nullptr, false, .25, -1, 1.);
     100         1541 :             tlNode->setName("tlLogic:" + *i);
     101         1541 :             root->addChild(tlNode);
     102              :             lastLane = lane;
     103              :         }
     104              :     }
     105          411 :     return root;
     106          822 : }
     107              : 
     108              : 
     109              : void
     110            0 : GUIOSGBuilder::buildLight(const GUISUMOAbstractView::Decal& d, osg::Group& addTo) {
     111              :     // each light must have a unique number
     112            0 :     osg::Light* light = new osg::Light(d.filename[5] - '0');
     113              :     // we set the light's position via a PositionAttitudeTransform object
     114              :     light->setPosition(osg::Vec4(0.0, 0.0, 0.0, 1.0));
     115              :     light->setDiffuse(osg::Vec4(1.0, 1.0, 1.0, 1.0));
     116              :     light->setSpecular(osg::Vec4(1.0, 1.0, 1.0, 1.0));
     117              :     light->setAmbient(osg::Vec4(1.0, 1.0, 1.0, 1.0));
     118              : 
     119            0 :     osg::LightSource* lightSource = new osg::LightSource();
     120            0 :     lightSource->setLight(light);
     121            0 :     lightSource->setLocalStateSetModes(osg::StateAttribute::ON);
     122            0 :     lightSource->setStateSetModes(*addTo.getOrCreateStateSet(), osg::StateAttribute::ON);
     123              : 
     124            0 :     osg::PositionAttitudeTransform* lightTransform = new osg::PositionAttitudeTransform();
     125            0 :     lightTransform->addChild(lightSource);
     126            0 :     lightTransform->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ));
     127              :     lightTransform->setScale(osg::Vec3d(0.1, 0.1, 0.1));
     128            0 :     addTo.addChild(lightTransform);
     129            0 : }
     130              : 
     131              : 
     132              : void
     133         4041 : GUIOSGBuilder::buildOSGEdgeGeometry(const MSEdge& edge,
     134              :                                     osg::Group& addTo,
     135              :                                     osgUtil::Tessellator& tessellator) {
     136              :     const std::vector<MSLane*>& lanes = edge.getLanes();
     137         9643 :     for (std::vector<MSLane*>::const_iterator j = lanes.begin(); j != lanes.end(); ++j) {
     138         5602 :         MSLane* l = (*j);
     139         5602 :         const bool extrude = edge.isWalkingArea() || isSidewalk(l->getPermissions());
     140         5602 :         const int geomFactor = (edge.isWalkingArea()) ? 1 : 2;
     141              :         const PositionVector& shape = l->getShape();
     142         5602 :         const int originalSize = (int)shape.size();
     143         5602 :         osg::Geode* geode = new osg::Geode();
     144         5602 :         osg::Geometry* geom = new osg::Geometry();
     145         5602 :         geode->addDrawable(geom);
     146         5602 :         geode->setName("lane:" + l->getID());
     147         5602 :         addTo.addChild(geode);
     148         5602 :         dynamic_cast<GUIGlObject*>(l)->setNode(geode);
     149         5602 :         const int upperShapeSize = originalSize * geomFactor;
     150         5602 :         const int totalShapeSize = (extrude) ? originalSize * 2 * geomFactor : originalSize * geomFactor;
     151         5602 :         const float zOffset = (extrude) ? (edge.isCrossing()) ? 0.01f : 0.1f : 0.f;
     152         5602 :         osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
     153              :         (*osg_colors)[0].set(128, 128, 128, 255);
     154         5602 :         geom->setColorArray(osg_colors, osg::Array::BIND_OVERALL);
     155         5602 :         osg::Vec3Array* osg_coords = new osg::Vec3Array(totalShapeSize);
     156         5602 :         geom->setVertexArray(osg_coords);
     157              :         int sizeDiff = 0;
     158         5602 :         if (edge.isWalkingArea()) {
     159          161 :             int index = upperShapeSize - 1;
     160         1098 :             for (int k = 0; k < upperShapeSize; ++k, --index) {
     161          937 :                 (*osg_coords)[index].set((float)shape[k].x(), (float)shape[k].y(), (float)shape[k].z() + zOffset);
     162              :             }
     163          322 :             geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, upperShapeSize));
     164              :         } else {
     165              :             int index = 0;
     166              :             PositionVector rshape = shape;
     167         5441 :             rshape.move2side(l->getWidth() / 2);
     168        16747 :             for (int k = (int)rshape.size() - 1; k >= 0; --k, ++index) {
     169        11306 :                 (*osg_coords)[index].set((float)rshape[k].x(), (float)rshape[k].y(), (float)rshape[k].z() + zOffset);
     170              :             }
     171              :             PositionVector lshape = shape;
     172         5441 :             lshape.move2side(-l->getWidth() / 2);
     173        16747 :             for (int k = 0; k < (int)lshape.size(); ++k, ++index) {
     174        11306 :                 (*osg_coords)[index].set((float)lshape[k].x(), (float)lshape[k].y(), (float)lshape[k].z() + zOffset);
     175              :             }
     176         5441 :             sizeDiff = (int)rshape.size() + (int)lshape.size() - upperShapeSize;
     177              :             int minSize = MIN2((int)rshape.size(), (int)lshape.size());
     178         5441 :             osg::DrawElementsUInt* surface = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP, 0);
     179        16747 :             for (int i = 0; i < minSize; ++i) {
     180        11306 :                 surface->push_back(i);
     181        11306 :                 surface->push_back(upperShapeSize + sizeDiff - 1 - i);
     182              :             }
     183         5441 :             geom->addPrimitiveSet(surface);
     184         5441 :         }
     185         5602 :         if (extrude) {
     186              :             int index = upperShapeSize;
     187         2334 :             for (int k = 0; k < upperShapeSize + sizeDiff; ++k, ++index) {
     188         1929 :                 (*osg_coords)[index].set((*osg_coords)[k].x(), (*osg_coords)[k].y(), (*osg_coords)[k].z() - zOffset);
     189              :             }
     190              :             // extrude edge to create the kerb
     191         2334 :             for (int i = 0; i < upperShapeSize + sizeDiff; ++i) {
     192         1929 :                 osg::Vec3 surfaceVec = (*osg_coords)[i] - (*osg_coords)[(i + 1) % (upperShapeSize + sizeDiff)];
     193         1929 :                 if (surfaceVec.length() > 0.) {
     194         1922 :                     osg::DrawElementsUInt* kerb = new osg::DrawElementsUInt(osg::PrimitiveSet::POLYGON, 0);
     195         1922 :                     kerb->push_back(i);
     196         1922 :                     kerb->push_back(upperShapeSize + i);
     197         1922 :                     kerb->push_back(upperShapeSize + (i + 1) % (upperShapeSize + sizeDiff));
     198         1922 :                     kerb->push_back((i + 1) % (upperShapeSize + sizeDiff));
     199         1922 :                     geom->addPrimitiveSet(kerb);
     200              :                 }
     201              :             }
     202              :         }
     203              : 
     204         5602 :         osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
     205         5602 :         ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
     206         5602 :         ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
     207              : 
     208         5602 :         if (shape.size() > 2) {
     209          324 :             tessellator.retessellatePolygons(*geom);
     210              : 
     211              : #ifdef DEBUG_TESSEL
     212              :             std::cout << "l=" << l->getID() << " origPoints=" << shape.size() << " geomSize=" << geom->getVertexArray()->getNumElements() << " points=";
     213              :             for (int i = 0; i < (int)geom->getVertexArray()->getNumElements(); i++) {
     214              :                 const osg::Vec3& p = (*((osg::Vec3Array*)geom->getVertexArray()))[i];
     215              :                 std::cout << p.x() << "," << p.y() << "," << p.z() << " ";
     216              :             }
     217              :             std::cout << "\n";
     218              : #endif
     219              :         }
     220         5602 :         osgUtil::SmoothingVisitor sv;
     221              : #if OSG_MIN_VERSION_REQUIRED(3,5,4)
     222              :         sv.setCreaseAngle(0.6 * osg::PI);
     223              : #endif
     224         5602 :         geom->accept(sv);
     225              :         static_cast<GUILane*>(l)->setGeometry(geom);
     226         5602 :     }
     227         4041 : }
     228              : 
     229              : 
     230              : void
     231         4459 : GUIOSGBuilder::buildOSGJunctionGeometry(GUIJunctionWrapper& junction,
     232              :                                         osg::Group& addTo,
     233              :                                         osgUtil::Tessellator& tessellator) {
     234              :     const PositionVector& shape = junction.getJunction().getShape();
     235         4459 :     osg::Geode* geode = new osg::Geode();
     236         4459 :     osg::Geometry* geom = new osg::Geometry();
     237         4459 :     geode->addDrawable(geom);
     238         4459 :     geode->setName("junction:" + junction.getMicrosimID());
     239         4459 :     addTo.addChild(geode);
     240         4459 :     dynamic_cast<GUIGlObject&>(junction).setNode(geode);
     241         4459 :     osg::Vec3Array* osg_coords = new osg::Vec3Array((int)shape.size()); // OSG needs float coordinates here
     242         4459 :     geom->setVertexArray(osg_coords);
     243        17596 :     for (int k = 0; k < (int)shape.size(); ++k) {
     244        13137 :         (*osg_coords)[k].set((float)shape[k].x(), (float)shape[k].y(), (float)shape[k].z());
     245              :     }
     246         4459 :     osg::Vec3Array* osg_normals = new osg::Vec3Array(1);
     247         4459 :     (*osg_normals)[0] = osg::Vec3(0, 0, 1); // OSG needs float coordinates here
     248         4459 :     geom->setNormalArray(osg_normals, osg::Array::BIND_PER_PRIMITIVE_SET);
     249         4459 :     osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
     250              :     (*osg_colors)[0].set(128, 128, 128, 255);
     251         4459 :     geom->setColorArray(osg_colors, osg::Array::BIND_OVERALL);
     252         4459 :     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, (int)shape.size()));
     253              : 
     254         4459 :     osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
     255         4459 :     ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
     256         4459 :     ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
     257              : 
     258         4459 :     if (shape.size() > 4) {
     259          918 :         tessellator.retessellatePolygons(*geom);
     260              :     }
     261              :     junction.setGeometry(geom);
     262         4459 : }
     263              : 
     264              : 
     265              : void
     266          105 : GUIOSGBuilder::buildTrafficLightDetails(MSTLLogicControl::TLSLogicVariants& vars, osg::Node* const tlg, osg::Node* const tly, osg::Node* const tlr, osg::Node* const tlu, osg::Node* poleBase, osg::Group& addTo) {
     267              :     // get the poleBase diameter for later repositioning
     268          105 :     osg::ComputeBoundsVisitor bboxCalc;
     269          105 :     poleBase->accept(bboxCalc);
     270          105 :     const double poleDiameter = bboxCalc.getBoundingBox().yMax() - bboxCalc.getBoundingBox().yMin();
     271          105 :     tlg->accept(bboxCalc);
     272          105 :     const double tlWidth = bboxCalc.getBoundingBox().yMax() - bboxCalc.getBoundingBox().yMin();
     273              : 
     274              :     // loop through lanes, collect edges, skip ped and bike infra for the time being
     275          105 :     MSTrafficLightLogic* tlLogic = vars.getActive();
     276              :     const MSTrafficLightLogic::LinkVectorVector& allLinks = tlLogic->getLinks();
     277              :     std::set<const MSEdge*> seenEdges;
     278              : 
     279         1646 :     for (const MSTrafficLightLogic::LinkVector& lv : allLinks) {
     280         3082 :         for (const MSLink* tlLink : lv) {
     281              :             // if not in seenEdges, create pole and reference it in the maps above
     282         1541 :             const MSEdge* approach = &tlLink->getLaneBefore()->getEdge();
     283         3014 :             if (!approach->isWalkingArea() && seenEdges.find(approach) != seenEdges.end()) {
     284         1116 :                 continue;
     285              :             }
     286          425 :             const std::vector<MSLane*> appLanes = approach->getLanes();
     287              :             // ref pos
     288              :             const double poleMinHeight = 5.;
     289              :             const double poleOffset = .5;
     290          425 :             double angle = 90. - appLanes[0]->getShape().rotationDegreeAtOffset(-1.);
     291          425 :             bool onlyPedCycle = isBikepath(approach->getPermissions()) || isSidewalk(approach->getPermissions());
     292          425 :             Position pos = appLanes[0]->getShape().back();
     293              :             double skipWidth = 0.;
     294              :             int firstSignalLaneIx = 0;
     295              :             std::vector<std::pair<osg::Group*, osg::Vec3d>> repeaters;
     296              :             // start with local coordinate system
     297          425 :             osg::PositionAttitudeTransform* appBase = new osg::PositionAttitudeTransform();
     298          425 :             osg::PositionAttitudeTransform* rightPoleBase = new osg::PositionAttitudeTransform();
     299          425 :             osg::PositionAttitudeTransform* rightPoleScaleNode = new osg::PositionAttitudeTransform();
     300          425 :             rightPoleScaleNode->addChild(poleBase);
     301          425 :             rightPoleBase->addChild(rightPoleScaleNode);
     302          425 :             appBase->addChild(rightPoleBase);
     303              :             rightPoleBase->setPosition(osg::Vec3d(pos.x(), pos.y(), pos.z()));
     304          850 :             rightPoleBase->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
     305          425 :                                                  0., osg::Vec3d(0, 1, 0),
     306          425 :                                                  DEG2RAD(angle), osg::Vec3d(0, 0, 1)));
     307          425 :             if (onlyPedCycle) { // pedestrian / cyclist signal only
     308           70 :                 rightPoleScaleNode->setScale(osg::Vec3d(.12 / poleDiameter, .12 / poleDiameter, 2.8));
     309           70 :                 if (approach->isCrossing()) { // center VRU signal pole at crossings
     310              :                     // move pole to the other side of the road
     311            0 :                     osg::Vec3d offset(cos(DEG2RAD(angle)), sin(DEG2RAD(angle)), 0.);
     312            0 :                     appBase->setPosition(appBase->getPosition() + offset * (poleOffset + approach->getLength()));
     313            0 :                     appBase->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
     314            0 :                                                    0., osg::Vec3d(0, 1, 0),
     315            0 :                                                    DEG2RAD(angle + 180), osg::Vec3d(0, 0, 1)));
     316           70 :                 } else if (approach->isWalkingArea()) { // pole for other direction > get position from crossing
     317           68 :                     pos = tlLink->getLane()->getShape().back();
     318           68 :                     angle = 90. - tlLink->getLane()->getShape().rotationDegreeAtOffset(-1.);
     319           68 :                     rightPoleBase->setPosition(osg::Vec3d(pos.x(), pos.y(), pos.z()) - osg::Vec3d(poleOffset * cos(DEG2RAD(angle)), poleOffset * sin(DEG2RAD(angle)), 0.));
     320           68 :                     rightPoleBase->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
     321           68 :                                                          0., osg::Vec3d(0, 1, 0),
     322           68 :                                                          DEG2RAD(angle), osg::Vec3d(0, 0, 1)));
     323           68 :                     if (tlLink->getLane()->getLinkCont()[0]->getTLIndex() < 0) { // check whether the other side is not specified explicitly
     324           68 :                         osg::PositionAttitudeTransform* leftPoleBase = new osg::PositionAttitudeTransform();
     325           68 :                         osg::PositionAttitudeTransform* leftPoleScaleNode = new osg::PositionAttitudeTransform();
     326           68 :                         appBase->addChild(leftPoleBase);
     327           68 :                         leftPoleScaleNode->addChild(poleBase);
     328              :                         leftPoleScaleNode->setScale(osg::Vec3d(.12 / poleDiameter, .12 / poleDiameter, 2.8));
     329           68 :                         leftPoleBase->addChild(leftPoleScaleNode);
     330           68 :                         double otherAngle = 90. - tlLink->getLane()->getShape().rotationDegreeAtOffset(1.);
     331           68 :                         Position otherPosRel = tlLink->getLane()->getShape().front();
     332              :                         osg::Vec3d leftPolePos(otherPosRel.x(), otherPosRel.y(), otherPosRel.z());
     333           68 :                         leftPoleBase->setPosition(leftPolePos + osg::Vec3d(poleOffset * cos(DEG2RAD(otherAngle)), poleOffset * sin(DEG2RAD(otherAngle)), 0.));
     334          136 :                         leftPoleBase->setAttitude(osg::Quat(0., osg::Vec3d(1., 0., 0.),
     335           68 :                                                             0., osg::Vec3d(0., 1., 0.),
     336          136 :                                                             DEG2RAD(angle + 180.), osg::Vec3d(0., 0., 1.)));
     337           68 :                         repeaters.push_back({ leftPoleBase, osg::Vec3d(0., 0., leftPoleBase->getPosition().z())});
     338              :                     }
     339              :                 } else {
     340            2 :                     double laneWidth = appLanes[0]->getWidth();
     341            2 :                     osg::Vec3d offset(-poleOffset * cos(DEG2RAD(angle)) - (.5 * laneWidth - skipWidth + poleOffset) * sin(DEG2RAD(angle)), poleOffset * sin(DEG2RAD(angle)) + (.5 * laneWidth - skipWidth + poleOffset) * cos(DEG2RAD(angle)), 0.);
     342              :                     rightPoleBase->setPosition(rightPoleBase->getPosition() + offset);
     343              :                 }
     344              :             } else {
     345              :                 // skip sidewalk and bike lane if leftmost lane is for cars
     346          355 :                 if (!noVehicles(appLanes.back()->getPermissions())) {
     347          495 :                     for (MSLane* appLane : appLanes) {
     348              :                         SVCPermissions permissions = appLane->getPermissions();
     349          495 :                         if (isSidewalk(permissions) || isForbidden(permissions)) {
     350          140 :                             skipWidth += appLane->getWidth();
     351              :                         } else {
     352              :                             break;
     353              :                         }
     354          140 :                         firstSignalLaneIx++;
     355              :                     }
     356              :                 }
     357          355 :                 const double laneWidth = appLanes[0]->getWidth();
     358          355 :                 const double horizontalWidth = approach->getWidth() - skipWidth;
     359          355 :                 const int laneCount = (int)appLanes.size() - firstSignalLaneIx;
     360          355 :                 osg::Vec3d offset(-poleOffset * cos(DEG2RAD(angle)) - (.5 * laneWidth - skipWidth + poleOffset) * sin(DEG2RAD(angle)), -poleOffset * sin(DEG2RAD(angle)) + (.5 * laneWidth - skipWidth + poleOffset) * cos(DEG2RAD(angle)), 0.);
     361              :                 rightPoleBase->setPosition(rightPoleBase->getPosition() + offset);
     362              : 
     363          355 :                 if (laneCount < 3) { // cantilever
     364          283 :                     const double cantiWidth = horizontalWidth - .1 * appLanes.back()->getWidth() + poleOffset;
     365          283 :                     const double holderWidth = cantiWidth - .4 * appLanes.back()->getWidth();
     366              :                     const double holderAngle = 7.5; // degrees
     367          283 :                     const double extraHeight = sin(DEG2RAD(holderAngle)) * holderWidth;
     368          283 :                     rightPoleScaleNode->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, poleMinHeight + extraHeight));
     369          283 :                     osg::PositionAttitudeTransform* cantileverBase = new osg::PositionAttitudeTransform();
     370              :                     cantileverBase->setPosition(osg::Vec3d(0., 0., poleMinHeight));
     371          283 :                     cantileverBase->setAttitude(osg::Quat(DEG2RAD(90.), osg::Vec3d(1, 0, 0),
     372          283 :                                                           0., osg::Vec3d(0, 1, 0),
     373          283 :                                                           0., osg::Vec3d(0, 0, 1)));
     374              :                     cantileverBase->setScale(osg::Vec3d(1., 1., cantiWidth));
     375          283 :                     cantileverBase->addChild(poleBase);
     376          283 :                     rightPoleBase->addChild(cantileverBase);
     377          283 :                     osg::PositionAttitudeTransform* cantileverHolderBase = new osg::PositionAttitudeTransform();
     378          283 :                     cantileverHolderBase->setPosition(osg::Vec3d(0., 0., poleMinHeight + extraHeight - .02));
     379          283 :                     cantileverHolderBase->setAttitude(osg::Quat(DEG2RAD(90. + holderAngle), osg::Vec3d(1, 0, 0),
     380          283 :                                                       0., osg::Vec3d(0, 1, 0),
     381          283 :                                                       0., osg::Vec3d(0, 0, 1)));
     382          283 :                     cantileverHolderBase->setScale(osg::Vec3d(.04 / poleDiameter, .04 / poleDiameter, sqrt(pow(holderWidth, 2.) + pow(extraHeight, 2.))));
     383          283 :                     cantileverHolderBase->addChild(poleBase);
     384          283 :                     rightPoleBase->addChild(cantileverHolderBase);
     385              :                 } else { // signal bridge
     386           72 :                     rightPoleScaleNode->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, poleMinHeight));
     387           72 :                     osg::PositionAttitudeTransform* leftPoleBase = new osg::PositionAttitudeTransform();
     388           72 :                     leftPoleBase->addChild(poleBase);
     389              :                     leftPoleBase->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, poleMinHeight));
     390           72 :                     osg::Vec3d leftPolePos = osg::Vec3d(0, -(horizontalWidth + 2. * poleOffset), 0.);
     391              :                     leftPoleBase->setPosition(leftPolePos);
     392           72 :                     rightPoleBase->addChild(leftPoleBase);
     393           72 :                     osg::PositionAttitudeTransform* bridgeBase = new osg::PositionAttitudeTransform();
     394              :                     bridgeBase->setPosition(osg::Vec3d(0., 0., poleMinHeight - .125));
     395           72 :                     bridgeBase->setAttitude(osg::Quat(DEG2RAD(90.), osg::Vec3d(1, 0, 0),
     396           72 :                                                       0., osg::Vec3d(0, 1, 0),
     397           72 :                                                       0., osg::Vec3d(0, 0, 1)));
     398              :                     bridgeBase->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, leftPolePos.length()));
     399           72 :                     bridgeBase->addChild(poleBase);
     400           72 :                     rightPoleBase->addChild(bridgeBase);
     401              :                 }
     402              :             }
     403              :             seenEdges.insert(approach);
     404              : 
     405              :             // Add signals and position them along the cantilever/bridge
     406              :             double refPos = poleOffset /*- skipWidth*/;
     407              :             std::vector<MSLane*>::const_iterator it = appLanes.begin();
     408         1030 :             for (std::advance(it, firstSignalLaneIx); it != appLanes.end(); it++) {
     409              :                 // get tlLinkIndices
     410          675 :                 const std::vector<MSLink*>& links = (*it)->getLinkCont();
     411              :                 std::set<int> tlIndices;
     412         2284 :                 for (MSLink* link : links) {
     413         1609 :                     if (link->getTLIndex() > -1) {
     414         1541 :                         tlIndices.insert(link->getTLIndex());
     415              :                     }
     416              :                 }
     417              :                 std::set<int> seenTlIndices;
     418              :                 bool placeRepeaters = true;
     419         2284 :                 for (MSLink* link : links) {
     420         1609 :                     std::vector<std::pair<osg::Group*, osg::Vec3d>> signalTransforms = { {rightPoleBase, osg::Vec3d(0., 0., 0.)} };
     421         1609 :                     if (placeRepeaters) {
     422          675 :                         signalTransforms.insert(signalTransforms.end(), repeaters.begin(), repeaters.end());
     423              :                         repeaters.clear();
     424              :                         placeRepeaters = false;
     425              :                     }
     426         1609 :                     int tlIndex = link->getTLIndex();
     427         3150 :                     if (tlIndex < 0 || seenTlIndices.find(tlIndex) != seenTlIndices.end()) {
     428              :                         continue;
     429              :                     }
     430         3150 :                     for (const std::pair<osg::Group*, osg::Vec3d>& transform : signalTransforms) {
     431              :                         GUISUMOAbstractView::Decal d;
     432         1609 :                         d.centerX = transform.second.x() + 0.15;
     433         1609 :                         d.centerY = (onlyPedCycle) ? 0. : -(refPos + .5 * (*it)->getWidth() - ((double)tlIndices.size() / 2. - 1. + (double)seenTlIndices.size()) * 1.5 * tlWidth);
     434         1609 :                         d.centerY += transform.second.y();
     435         3080 :                         d.centerZ = (onlyPedCycle) ? 2.2 : 3.8;
     436         1609 :                         d.centerZ += transform.second.z();
     437         3080 :                         d.altitude = (onlyPedCycle) ? 0.6 : -1;
     438         1609 :                         osg::PositionAttitudeTransform* tlNode = getTrafficLight(d, vars, links[0], tlg, tly, tlr, tlu, poleBase, false);
     439         1609 :                         tlNode->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
     440         1609 :                                                       0., osg::Vec3d(0, 1, 0),
     441            0 :                                                       DEG2RAD(180.0), osg::Vec3d(0, 0, 1)));
     442         1609 :                         transform.first->addChild(tlNode);
     443              :                     }
     444              :                     seenTlIndices.insert(tlIndex);
     445         1609 :                 }
     446              :                 // only one signal for bike/pedestrian only edges
     447          675 :                 if (onlyPedCycle) {
     448              :                     break;
     449              :                 }
     450          605 :                 refPos += (*it)->getWidth();
     451              :             }
     452              :             // interaction
     453              :             appBase->setNodeMask(GUIOSGView::NODESET_TLSMODELS);
     454          425 :             appBase->setName("tlLogic:" + tlLogic->getID());
     455          425 :             addTo.addChild(appBase);
     456          425 :         }
     457              :     }
     458          105 : }
     459              : 
     460              : 
     461              : void
     462            0 : GUIOSGBuilder::buildDecal(const GUISUMOAbstractView::Decal& d, osg::Group& addTo) {
     463            0 :     osg::Node* pLoadedModel = osgDB::readNodeFile(d.filename);
     464            0 :     osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
     465              :     double zOffset = 0.;
     466            0 :     if (pLoadedModel == nullptr) {
     467              :         // check for 2D image
     468            0 :         osg::Image* pImage = osgDB::readImageFile(d.filename);
     469            0 :         if (pImage == nullptr) {
     470              :             base = nullptr;
     471            0 :             WRITE_ERRORF(TL("Could not load '%'."), d.filename);
     472            0 :             return;
     473              :         }
     474            0 :         osg::Texture2D* texture = new osg::Texture2D();
     475            0 :         texture->setImage(pImage);
     476            0 :         osg::Geometry* quad = osg::createTexturedQuadGeometry(osg::Vec3d(-0.5 * d.width, -0.5 * d.height, 0.), osg::Vec3d(d.width, 0., 0.), osg::Vec3d(0., d.height, 0.));
     477            0 :         quad->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture);
     478            0 :         osg::Geode* const pModel = new osg::Geode();
     479            0 :         pModel->addDrawable(quad);
     480            0 :         base->addChild(pModel);
     481            0 :         zOffset = d.layer;
     482              :     } else {
     483            0 :         osg::ShadeModel* sm = new osg::ShadeModel();
     484              :         sm->setMode(osg::ShadeModel::FLAT);
     485            0 :         pLoadedModel->getOrCreateStateSet()->setAttribute(sm);
     486            0 :         base->addChild(pLoadedModel);
     487              :     }
     488            0 :     osg::ComputeBoundsVisitor bboxCalc;
     489            0 :     base->accept(bboxCalc);
     490              :     const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
     491            0 :     WRITE_MESSAGEF(TL("Loaded decal '%' with bounding box % %."), d.filename, toString(Position(bbox.xMin(), bbox.yMin(), bbox.zMin())), toString(Position(bbox.xMax(), bbox.yMax(), bbox.zMax())));
     492            0 :     double xScale = d.width > 0 ? d.width / (bbox.xMax() - bbox.xMin()) : 1.;
     493            0 :     double yScale = d.height > 0 ? d.height / (bbox.yMax() - bbox.yMin()) : 1.;
     494            0 :     const double zScale = d.altitude > 0 ? d.altitude / (bbox.zMax() - bbox.zMin()) : 1.;
     495            0 :     if (d.width < 0 && d.height < 0 && d.altitude > 0) {
     496              :         xScale = yScale = zScale;
     497              :     }
     498              :     base->setScale(osg::Vec3d(xScale, yScale, zScale));
     499            0 :     base->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ + zOffset));
     500            0 :     base->setAttitude(osg::Quat(osg::DegreesToRadians(d.roll), osg::Vec3d(1, 0, 0),
     501            0 :                                 osg::DegreesToRadians(d.tilt), osg::Vec3d(0, 1, 0),
     502            0 :                                 osg::DegreesToRadians(d.rot), osg::Vec3d(0, 0, 1)));
     503            0 :     addTo.addChild(base);
     504            0 : }
     505              : 
     506              : 
     507              : osg::PositionAttitudeTransform*
     508         3150 : GUIOSGBuilder::getTrafficLight(const GUISUMOAbstractView::Decal& d, MSTLLogicControl::TLSLogicVariants& vars, const MSLink* link, osg::Node* const tlg, osg::Node* const tly, osg::Node* const tlr, osg::Node* const tlu, osg::Node* const pole, const bool withPole, const double size, double poleHeight, double transparency) {
     509         3150 :     osg::PositionAttitudeTransform* ret = new osg::PositionAttitudeTransform();
     510              :     double xScale = 1., yScale = 1., zScale = 1.;
     511         3150 :     if (tlg != nullptr) {
     512         1609 :         osg::ComputeBoundsVisitor bboxCalc;
     513         1609 :         tlg->accept(bboxCalc);
     514              :         const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
     515         1609 :         xScale = d.width > 0 ? d.width / (bbox.xMax() - bbox.xMin()) : 1.;
     516         1609 :         yScale = d.height > 0 ? d.height / (bbox.yMax() - bbox.yMin()) : 1.;
     517         1609 :         double addHeight = (withPole) ? poleHeight : 0.;
     518         1609 :         zScale = d.altitude > 0 ? d.altitude / (addHeight + bbox.zMax() - bbox.zMin()) : 1.;
     519         1609 :     }
     520         3150 :     if (d.width < 0 && d.height < 0 && d.altitude > 0) {
     521              :         xScale = yScale = zScale;
     522              :     }
     523         3150 :     osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
     524         3150 :     osg::Switch* switchNode = new osg::Switch();
     525         3150 :     switchNode->addChild(createTrafficLightState(d, tlg, withPole, size, osg::Vec4d(0., 1., 0., transparency)));
     526         3150 :     switchNode->addChild(createTrafficLightState(d, tly, withPole, size, osg::Vec4d(1., 1., 0., transparency)));
     527         3150 :     switchNode->addChild(createTrafficLightState(d, tlr, withPole, size, osg::Vec4d(1., 0., 0., transparency)));
     528         3150 :     switchNode->addChild(createTrafficLightState(d, tlu, withPole, size, osg::Vec4d(1., .5, 0., transparency)));
     529         3150 :     base->addChild(switchNode);
     530         3150 :     vars.addSwitchCommand(new GUIOSGView::Command_TLSChange(link, switchNode));
     531         3150 :     if (withPole) {
     532              :         base->setPosition(osg::Vec3d(0., 0., poleHeight));
     533            0 :         osg::PositionAttitudeTransform* poleBase = new osg::PositionAttitudeTransform();
     534            0 :         poleBase->addChild(pole);
     535              :         poleBase->setScale(osg::Vec3d(1., 1., poleHeight));
     536            0 :         ret->addChild(poleBase);
     537              :     }
     538         6300 :     ret->setAttitude(osg::Quat(osg::DegreesToRadians(d.roll), osg::Vec3(1, 0, 0),
     539         3150 :                                osg::DegreesToRadians(d.tilt), osg::Vec3(0, 1, 0),
     540         3150 :                                osg::DegreesToRadians(d.rot), osg::Vec3(0, 0, 1)));
     541         3150 :     ret->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ));
     542              :     ret->setScale(osg::Vec3d(xScale, yScale, zScale));
     543         3150 :     ret->addChild(base);
     544         3150 :     return ret;
     545              : }
     546              : 
     547              : 
     548              : osg::PositionAttitudeTransform*
     549        12600 : GUIOSGBuilder::createTrafficLightState(const GUISUMOAbstractView::Decal& d, osg::Node* tl, const double withPole, const double size, osg::Vec4d color) {
     550        12600 :     osg::PositionAttitudeTransform* ret = new osg::PositionAttitudeTransform();
     551        12600 :     if (tl != nullptr) {
     552         6436 :         ret->addChild(tl);
     553              :     }
     554        12600 :     if (size > 0.) {
     555         6164 :         unsigned int nodeMask = (withPole) ? GUIOSGView::NodeSetGroup::NODESET_TLSDOMES : GUIOSGView::NodeSetGroup::NODESET_TLSLINKMARKERS;
     556         6164 :         osg::Geode* geode = new osg::Geode();
     557         6164 :         osg::Vec3d center = osg::Vec3d(0., 0., (withPole) ? -1.8 : 0.);
     558         6164 :         osg::ShapeDrawable* shape = new osg::ShapeDrawable(new osg::Sphere(center, (float)size));
     559         6164 :         geode->addDrawable(shape);
     560         6164 :         osg::ref_ptr<osg::StateSet> ss = shape->getOrCreateStateSet();
     561         6164 :         ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
     562         6164 :         ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
     563         6164 :         shape->setColor(color);
     564         6164 :         osg::PositionAttitudeTransform* ellipse = new osg::PositionAttitudeTransform();
     565         6164 :         ellipse->addChild(geode);
     566              :         ellipse->setPosition(center);
     567              :         ellipse->setPivotPoint(center);
     568         6164 :         if (withPole) {
     569            0 :             ellipse->setScale(osg::Vec3d(4., 4., 2.5 * d.altitude + 1.1));
     570              :         } else {
     571              :             ellipse->setScale(osg::Vec3d(4., 4., 1.1));
     572              :         }
     573              :         ellipse->setNodeMask(nodeMask);
     574         6164 :         ret->addChild(ellipse);
     575              :     }
     576        12600 :     return ret;
     577              : }
     578              : 
     579              : 
     580              : void
     581        87036 : GUIOSGBuilder::setShapeState(osg::ref_ptr<osg::ShapeDrawable> shape) {
     582        87036 :     osg::ref_ptr<osg::StateSet> ss = shape->getOrCreateStateSet();
     583        87036 :     ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
     584        87036 :     ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
     585        87036 : }
     586              : 
     587              : 
     588              : GUIOSGView::OSGMovable
     589        29364 : GUIOSGBuilder::buildMovable(const MSVehicleType& type) {
     590              :     GUIOSGView::OSGMovable m;
     591        29364 :     m.pos = new osg::PositionAttitudeTransform();
     592              :     double enlarge = 0.05;
     593              :     const std::string& osgFile = type.getOSGFile();
     594        29364 :     if (myCars.find(osgFile) == myCars.end()) {
     595          273 :         myCars[osgFile] = osgDB::readNodeFile(osgFile);
     596          273 :         if (myCars[osgFile] == 0) {
     597            0 :             WRITE_ERRORF(TL("Could not load '%'. The model is replaced by a cone shape."), osgFile);
     598            0 :             osg::PositionAttitudeTransform* rot = new osg::PositionAttitudeTransform();
     599            0 :             rot->addChild(new osg::ShapeDrawable(new osg::Cone(osg::Vec3d(0, 0, 0), 1.0f, 1.0f)));
     600            0 :             rot->setAttitude(osg::Quat(osg::DegreesToRadians(90.), osg::Vec3(1, 0, 0),
     601            0 :                                        0., osg::Vec3(0, 1, 0),
     602            0 :                                        0., osg::Vec3(0, 0, 1)));
     603            0 :             myCars[osgFile] = rot;
     604              :         }
     605              :     }
     606        29364 :     osg::Node* carNode = myCars[osgFile];
     607        29364 :     if (carNode != nullptr) {
     608        29364 :         osg::ComputeBoundsVisitor bboxCalc;
     609        29364 :         carNode->accept(bboxCalc);
     610              :         const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
     611        29364 :         osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
     612        29364 :         base->addChild(carNode);
     613        29364 :         base->setPivotPoint(osg::Vec3d((bbox.xMin() + bbox.xMax()) / 2., bbox.yMin(), bbox.zMin()));
     614        29364 :         base->setScale(osg::Vec3d(type.getWidth() / (bbox.xMax() - bbox.xMin()),
     615        29364 :                                   type.getLength() / (bbox.yMax() - bbox.yMin()),
     616        29364 :                                   type.getHeight() / (bbox.zMax() - bbox.zMin())));
     617        29364 :         m.pos->addChild(base);
     618              : 
     619              :         // material for coloring the person or vehicle body
     620        29364 :         m.mat = new osg::Material();
     621        29364 :         osg::ref_ptr<osg::StateSet> ss = base->getOrCreateStateSet();
     622              :         ss->setAttribute(m.mat, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
     623        29364 :         ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
     624        29364 :         ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
     625        29364 :     }
     626        29364 :     if (type.getVehicleClass() != SVC_PEDESTRIAN) {
     627        29012 :         m.lights = new osg::Switch();
     628        87036 :         for (double sideFactor = -1.; sideFactor < 2.5; sideFactor += 2.) {
     629        58024 :             osg::Geode* geode = new osg::Geode();
     630        58024 :             osg::ShapeDrawable* right = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3d((type.getWidth() / 2. + enlarge)*sideFactor, 0., type.getHeight() / 2.), 0.2f));
     631        58024 :             geode->addDrawable(right);
     632              :             //pat->addChild(geode);
     633       116048 :             setShapeState(right);
     634        58024 :             right->setColor(osg::Vec4(1.f, .5f, 0.f, .8f));
     635        58024 :             osg::Sequence* seq = new osg::Sequence();
     636              :             // Wikipedia says about 1.5Hz
     637        58024 :             seq->addChild(geode, .33);
     638        58024 :             seq->addChild(new osg::Geode(), .33);
     639              :             // loop through all children
     640        58024 :             seq->setInterval(osg::Sequence::LOOP, 0, -1);
     641              :             // real-time playback, repeat indefinitely
     642        58024 :             seq->setDuration(1.0f, -1);
     643              :             // must be started explicitly
     644        58024 :             seq->setMode(osg::Sequence::START);
     645        58024 :             m.lights->addChild(seq);
     646              :         }
     647        29012 :         osg::Geode* geode = new osg::Geode();
     648        29012 :         osg::CompositeShape* comp = new osg::CompositeShape();
     649        29012 :         comp->addChild(new osg::Sphere(osg::Vec3d(-(type.getWidth() / 2. + enlarge), type.getLength() + enlarge, type.getHeight() / 2.), .2f));
     650        29012 :         comp->addChild(new osg::Sphere(osg::Vec3d(type.getWidth() / 2. + enlarge, type.getLength() + enlarge, type.getHeight() / 2.), .2f));
     651        29012 :         osg::ShapeDrawable* brake = new osg::ShapeDrawable(comp);
     652        29012 :         brake->setColor(osg::Vec4(1.f, 0.f, 0.f, .8f));
     653        29012 :         geode->addDrawable(brake);
     654        58024 :         setShapeState(brake);
     655        29012 :         m.lights->addChild(geode);
     656              : 
     657        29012 :         osg::Vec3d center(0, -type.getLength() / 2., 0.);
     658        29012 :         osg::PositionAttitudeTransform* ellipse = new osg::PositionAttitudeTransform();
     659        29012 :         ellipse->addChild(geode);
     660              :         ellipse->addChild(m.lights);
     661              :         ellipse->setPivotPoint(center);
     662              :         ellipse->setPosition(center);
     663        29012 :         m.pos->addChild(ellipse);
     664              :     }
     665        29364 :     m.active = true;
     666        29364 :     return m;
     667            0 : }
     668              : 
     669              : 
     670              : osg::Node*
     671          411 : GUIOSGBuilder::buildPlane(const float length) {
     672          411 :     osg::Geode* geode = new osg::Geode();
     673          411 :     osg::Geometry* geom = new osg::Geometry;
     674          411 :     geode->addDrawable(geom);
     675          411 :     osg::Vec3Array* coords = new osg::Vec3Array(4); // OSG needs float coordinates here
     676          411 :     geom->setVertexArray(coords);
     677          411 :     (*coords)[0].set(.5f * length, .5f * length, -0.1f);
     678              :     (*coords)[1].set(.5f * length, -.5f * length, -0.1f);
     679              :     (*coords)[2].set(-.5f * length, -.5f * length, -0.1f);
     680              :     (*coords)[3].set(-.5f * length, .5f * length, -0.1f);
     681          411 :     osg::Vec3Array* normals = new osg::Vec3Array(1); // OSG needs float coordinates here
     682              :     (*normals)[0].set(0, 0, 1);
     683          411 :     geom->setNormalArray(normals, osg::Array::BIND_PER_PRIMITIVE_SET);
     684          411 :     osg::Vec4ubArray* colors = new osg::Vec4ubArray(1);
     685              :     (*colors)[0].set(0, 255, 0, 255);
     686          411 :     geom->setColorArray(colors, osg::Array::BIND_OVERALL);
     687          822 :     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 4));
     688              : 
     689          411 :     osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
     690          411 :     ss->setRenderingHint(osg::StateSet::OPAQUE_BIN);
     691          411 :     ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
     692              : 
     693          411 :     return geode;
     694              : }
     695              : 
     696              : 
     697              : #endif
     698              : 
     699              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1