Eclipse SUMO - Simulation of Urban MObility
GUIOSGBuilder.cpp
Go to the documentation of this file.
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 /****************************************************************************/
20 // Builds OSG nodes from microsim objects
21 /****************************************************************************/
22 #include <config.h>
23 
24 #ifdef HAVE_OSG
25 
26 #include <guisim/GUIEdge.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>
34 #include <microsim/MSLane.h>
35 #include <microsim/MSNet.h>
36 #include <microsim/MSVehicleType.h>
41 #include <utils/geom/GeomHelper.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 GUIOSGBuilder::buildOSGScene(osg::Node* const tlg, osg::Node* const tly, osg::Node* const tlr, osg::Node* const tlu, osg::Node* const pole) {
61  osgUtil::Tessellator tesselator;
62  osg::Group* root = new osg::Group();
63  GUINet* net = static_cast<GUINet*>(MSNet::getInstance());
64  // build edges
65  for (const MSEdge* e : net->getEdgeControl().getEdges()) {
66  if (!e->isInternal()) {
67  buildOSGEdgeGeometry(*e, *root, tesselator);
68  }
69  }
70  // build junctions
71  for (int index = 0; index < (int)net->myJunctionWrapper.size(); ++index) {
72  buildOSGJunctionGeometry(*net->myJunctionWrapper[index], *root, tesselator);
73  }
74  // build traffic lights
76  const std::vector<std::string> tlids = net->getTLSControl().getAllTLIds();
77  for (std::vector<std::string>::const_iterator i = tlids.begin(); i != tlids.end(); ++i) {
79  buildTrafficLightDetails(vars, tlg, tly, tlr, tlu, pole, *root);
80 
82  const MSLane* lastLane = 0;
83  int idx = 0;
84  for (MSTrafficLightLogic::LaneVectorVector::const_iterator j = lanes.begin(); j != lanes.end(); ++j, ++idx) {
85  if ((*j).size() == 0) {
86  continue;
87  }
88  const MSLane* const lane = (*j)[0];
89  const Position pos = lane->getShape().back();
90  const double angle = osg::DegreesToRadians(lane->getShape().rotationDegreeAtOffset(-1.) + 90.);
91  d.centerZ = pos.z() + 4.;
92  if (lane == lastLane) {
93  d.centerX += 1.2 * sin(angle);
94  d.centerY += 1.2 * cos(angle);
95  } else {
96  d.centerX = pos.x() - 1.5 * sin(angle);
97  d.centerY = pos.y() - 1.5 * cos(angle);
98  }
99  osg::PositionAttitudeTransform* tlNode = getTrafficLight(d, vars, vars.getActive()->getLinksAt(idx)[0], nullptr, nullptr, nullptr, nullptr, nullptr, false, .25, -1, 1.);
100  tlNode->setName("tlLogic:" + *i);
101  root->addChild(tlNode);
102  lastLane = lane;
103  }
104  }
105  return root;
106 }
107 
108 
109 void
110 GUIOSGBuilder::buildLight(const GUISUMOAbstractView::Decal& d, osg::Group& addTo) {
111  // each light must have a unique number
112  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  osg::LightSource* lightSource = new osg::LightSource();
120  lightSource->setLight(light);
121  lightSource->setLocalStateSetModes(osg::StateAttribute::ON);
122  lightSource->setStateSetModes(*addTo.getOrCreateStateSet(), osg::StateAttribute::ON);
123 
124  osg::PositionAttitudeTransform* lightTransform = new osg::PositionAttitudeTransform();
125  lightTransform->addChild(lightSource);
126  lightTransform->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ));
127  lightTransform->setScale(osg::Vec3d(0.1, 0.1, 0.1));
128  addTo.addChild(lightTransform);
129 }
130 
131 
132 void
133 GUIOSGBuilder::buildOSGEdgeGeometry(const MSEdge& edge,
134  osg::Group& addTo,
135  osgUtil::Tessellator& tessellator) {
136  const std::vector<MSLane*>& lanes = edge.getLanes();
137  for (std::vector<MSLane*>::const_iterator j = lanes.begin(); j != lanes.end(); ++j) {
138  MSLane* l = (*j);
139  const bool extrude = edge.isWalkingArea() || isSidewalk(l->getPermissions());
140  const int geomFactor = (edge.isWalkingArea()) ? 1 : 2;
141  const PositionVector& shape = l->getShape();
142  const int originalSize = (int)shape.size();
143  osg::Geode* geode = new osg::Geode();
144  osg::Geometry* geom = new osg::Geometry();
145  geode->addDrawable(geom);
146  geode->setName("lane:" + l->getID());
147  addTo.addChild(geode);
148  dynamic_cast<GUIGlObject*>(l)->setNode(geode);
149  const int upperShapeSize = originalSize * geomFactor;
150  const int totalShapeSize = (extrude) ? originalSize * 2 * geomFactor : originalSize * geomFactor;
151  const float zOffset = (extrude) ? (edge.isCrossing()) ? 0.01f : 0.1f : 0.f;
152  osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
153  (*osg_colors)[0].set(128, 128, 128, 255);
154  geom->setColorArray(osg_colors, osg::Array::BIND_OVERALL);
155  osg::Vec3Array* osg_coords = new osg::Vec3Array(totalShapeSize);
156  geom->setVertexArray(osg_coords);
157  int sizeDiff = 0;
158  if (edge.isWalkingArea()) {
159  int index = upperShapeSize - 1;
160  for (int k = 0; k < upperShapeSize; ++k, --index) {
161  (*osg_coords)[index].set((float)shape[k].x(), (float)shape[k].y(), (float)shape[k].z() + zOffset);
162  }
163  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, upperShapeSize));
164  } else {
165  int index = 0;
166  PositionVector rshape = shape;
167  rshape.move2side(l->getWidth() / 2);
168  for (int k = (int)rshape.size() - 1; k >= 0; --k, ++index) {
169  (*osg_coords)[index].set((float)rshape[k].x(), (float)rshape[k].y(), (float)rshape[k].z() + zOffset);
170  }
171  PositionVector lshape = shape;
172  lshape.move2side(-l->getWidth() / 2);
173  for (int k = 0; k < (int)lshape.size(); ++k, ++index) {
174  (*osg_coords)[index].set((float)lshape[k].x(), (float)lshape[k].y(), (float)lshape[k].z() + zOffset);
175  }
176  sizeDiff = (int)rshape.size() + (int)lshape.size() - upperShapeSize;
177  int minSize = MIN2((int)rshape.size(), (int)lshape.size());
178  osg::DrawElementsUInt* surface = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP, 0);
179  for (int i = 0; i < minSize; ++i) {
180  surface->push_back(i);
181  surface->push_back(upperShapeSize + sizeDiff - 1 - i);
182  }
183  geom->addPrimitiveSet(surface);
184  }
185  if (extrude) {
186  int index = upperShapeSize;
187  for (int k = 0; k < upperShapeSize + sizeDiff; ++k, ++index) {
188  (*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  for (int i = 0; i < upperShapeSize + sizeDiff; ++i) {
192  osg::Vec3 surfaceVec = (*osg_coords)[i] - (*osg_coords)[(i + 1) % (upperShapeSize + sizeDiff)];
193  if (surfaceVec.length() > 0.) {
194  osg::DrawElementsUInt* kerb = new osg::DrawElementsUInt(osg::PrimitiveSet::POLYGON, 0);
195  kerb->push_back(i);
196  kerb->push_back(upperShapeSize + i);
197  kerb->push_back(upperShapeSize + (i + 1) % (upperShapeSize + sizeDiff));
198  kerb->push_back((i + 1) % (upperShapeSize + sizeDiff));
199  geom->addPrimitiveSet(kerb);
200  }
201  }
202  }
203 
204  osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
205  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
206  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
207 
208  if (shape.size() > 2) {
209  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  osgUtil::SmoothingVisitor sv;
221 #if OSG_MIN_VERSION_REQUIRED(3,5,4)
222  sv.setCreaseAngle(0.6 * osg::PI);
223 #endif
224  geom->accept(sv);
225  static_cast<GUILane*>(l)->setGeometry(geom);
226  }
227 }
228 
229 
230 void
231 GUIOSGBuilder::buildOSGJunctionGeometry(GUIJunctionWrapper& junction,
232  osg::Group& addTo,
233  osgUtil::Tessellator& tessellator) {
234  const PositionVector& shape = junction.getJunction().getShape();
235  osg::Geode* geode = new osg::Geode();
236  osg::Geometry* geom = new osg::Geometry();
237  geode->addDrawable(geom);
238  geode->setName("junction:" + junction.getMicrosimID());
239  addTo.addChild(geode);
240  dynamic_cast<GUIGlObject&>(junction).setNode(geode);
241  osg::Vec3Array* osg_coords = new osg::Vec3Array((int)shape.size()); // OSG needs float coordinates here
242  geom->setVertexArray(osg_coords);
243  for (int k = 0; k < (int)shape.size(); ++k) {
244  (*osg_coords)[k].set((float)shape[k].x(), (float)shape[k].y(), (float)shape[k].z());
245  }
246  osg::Vec3Array* osg_normals = new osg::Vec3Array(1);
247  (*osg_normals)[0] = osg::Vec3(0, 0, 1); // OSG needs float coordinates here
248  geom->setNormalArray(osg_normals, osg::Array::BIND_PER_PRIMITIVE_SET);
249  osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
250  (*osg_colors)[0].set(128, 128, 128, 255);
251  geom->setColorArray(osg_colors, osg::Array::BIND_OVERALL);
252  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, (int)shape.size()));
253 
254  osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
255  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
256  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
257 
258  if (shape.size() > 4) {
259  tessellator.retessellatePolygons(*geom);
260  }
261  junction.setGeometry(geom);
262 }
263 
264 
265 void
266 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  osg::ComputeBoundsVisitor bboxCalc;
269  poleBase->accept(bboxCalc);
270  const double poleDiameter = bboxCalc.getBoundingBox().yMax() - bboxCalc.getBoundingBox().yMin();
271  tlg->accept(bboxCalc);
272  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  MSTrafficLightLogic* tlLogic = vars.getActive();
276  const MSTrafficLightLogic::LinkVectorVector& allLinks = tlLogic->getLinks();
277  std::set<const MSEdge*> seenEdges;
278 
279  for (const MSTrafficLightLogic::LinkVector& lv : allLinks) {
280  for (const MSLink* tlLink : lv) {
281  // if not in seenEdges, create pole and reference it in the maps above
282  const MSEdge* approach = &tlLink->getLaneBefore()->getEdge();
283  if (!approach->isWalkingArea() && seenEdges.find(approach) != seenEdges.end()) {
284  continue;
285  }
286  const std::vector<MSLane*> appLanes = approach->getLanes();
287  // ref pos
288  const double poleMinHeight = 5.;
289  const double poleOffset = .5;
290  double angle = 90. - appLanes[0]->getShape().rotationDegreeAtOffset(-1.);
291  bool onlyPedCycle = isBikepath(approach->getPermissions()) || isSidewalk(approach->getPermissions());
292  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  osg::PositionAttitudeTransform* appBase = new osg::PositionAttitudeTransform();
298  osg::PositionAttitudeTransform* rightPoleBase = new osg::PositionAttitudeTransform();
299  osg::PositionAttitudeTransform* rightPoleScaleNode = new osg::PositionAttitudeTransform();
300  rightPoleScaleNode->addChild(poleBase);
301  rightPoleBase->addChild(rightPoleScaleNode);
302  appBase->addChild(rightPoleBase);
303  rightPoleBase->setPosition(osg::Vec3d(pos.x(), pos.y(), pos.z()));
304  rightPoleBase->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
305  0., osg::Vec3d(0, 1, 0),
306  DEG2RAD(angle), osg::Vec3d(0, 0, 1)));
307  if (onlyPedCycle) { // pedestrian / cyclist signal only
308  rightPoleScaleNode->setScale(osg::Vec3d(.12 / poleDiameter, .12 / poleDiameter, 2.8));
309  if (approach->isCrossing()) { // center VRU signal pole at crossings
310  // move pole to the other side of the road
311  osg::Vec3d offset(cos(DEG2RAD(angle)), sin(DEG2RAD(angle)), 0.);
312  appBase->setPosition(appBase->getPosition() + offset * (poleOffset + approach->getLength()));
313  appBase->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
314  0., osg::Vec3d(0, 1, 0),
315  DEG2RAD(angle + 180), osg::Vec3d(0, 0, 1)));
316  } else if (approach->isWalkingArea()) { // pole for other direction > get position from crossing
317  pos = tlLink->getLane()->getShape().back();
318  angle = 90. - tlLink->getLane()->getShape().rotationDegreeAtOffset(-1.);
319  rightPoleBase->setPosition(osg::Vec3d(pos.x(), pos.y(), pos.z()) - osg::Vec3d(poleOffset * cos(DEG2RAD(angle)), poleOffset * sin(DEG2RAD(angle)), 0.));
320  rightPoleBase->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
321  0., osg::Vec3d(0, 1, 0),
322  DEG2RAD(angle), osg::Vec3d(0, 0, 1)));
323  if (tlLink->getLane()->getLinkCont()[0]->getTLIndex() < 0) { // check whether the other side is not specified explicitly
324  osg::PositionAttitudeTransform* leftPoleBase = new osg::PositionAttitudeTransform();
325  osg::PositionAttitudeTransform* leftPoleScaleNode = new osg::PositionAttitudeTransform();
326  appBase->addChild(leftPoleBase);
327  leftPoleScaleNode->addChild(poleBase);
328  leftPoleScaleNode->setScale(osg::Vec3d(.12 / poleDiameter, .12 / poleDiameter, 2.8));
329  leftPoleBase->addChild(leftPoleScaleNode);
330  double otherAngle = 90. - tlLink->getLane()->getShape().rotationDegreeAtOffset(1.);
331  Position otherPosRel = tlLink->getLane()->getShape().front();
332  osg::Vec3d leftPolePos(otherPosRel.x(), otherPosRel.y(), otherPosRel.z());
333  leftPoleBase->setPosition(leftPolePos + osg::Vec3d(poleOffset * cos(DEG2RAD(otherAngle)), poleOffset * sin(DEG2RAD(otherAngle)), 0.));
334  leftPoleBase->setAttitude(osg::Quat(0., osg::Vec3d(1., 0., 0.),
335  0., osg::Vec3d(0., 1., 0.),
336  DEG2RAD(angle + 180.), osg::Vec3d(0., 0., 1.)));
337  repeaters.push_back({ leftPoleBase, osg::Vec3d(0., 0., leftPoleBase->getPosition().z())});
338  }
339  } else {
340  double laneWidth = appLanes[0]->getWidth();
341  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  if (!noVehicles(appLanes.back()->getPermissions())) {
347  for (MSLane* appLane : appLanes) {
348  SVCPermissions permissions = appLane->getPermissions();
349  if (isSidewalk(permissions) || isForbidden(permissions)) {
350  skipWidth += appLane->getWidth();
351  } else {
352  break;
353  }
354  firstSignalLaneIx++;
355  }
356  }
357  const double laneWidth = appLanes[0]->getWidth();
358  const double horizontalWidth = approach->getWidth() - skipWidth;
359  const int laneCount = (int)appLanes.size() - firstSignalLaneIx;
360  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  if (laneCount < 3) { // cantilever
364  const double cantiWidth = horizontalWidth - .1 * appLanes.back()->getWidth() + poleOffset;
365  const double holderWidth = cantiWidth - .4 * appLanes.back()->getWidth();
366  const double holderAngle = 7.5; // degrees
367  const double extraHeight = sin(DEG2RAD(holderAngle)) * holderWidth;
368  rightPoleScaleNode->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, poleMinHeight + extraHeight));
369  osg::PositionAttitudeTransform* cantileverBase = new osg::PositionAttitudeTransform();
370  cantileverBase->setPosition(osg::Vec3d(0., 0., poleMinHeight));
371  cantileverBase->setAttitude(osg::Quat(DEG2RAD(90.), osg::Vec3d(1, 0, 0),
372  0., osg::Vec3d(0, 1, 0),
373  0., osg::Vec3d(0, 0, 1)));
374  cantileverBase->setScale(osg::Vec3d(1., 1., cantiWidth));
375  cantileverBase->addChild(poleBase);
376  rightPoleBase->addChild(cantileverBase);
377  osg::PositionAttitudeTransform* cantileverHolderBase = new osg::PositionAttitudeTransform();
378  cantileverHolderBase->setPosition(osg::Vec3d(0., 0., poleMinHeight + extraHeight - .02));
379  cantileverHolderBase->setAttitude(osg::Quat(DEG2RAD(90. + holderAngle), osg::Vec3d(1, 0, 0),
380  0., osg::Vec3d(0, 1, 0),
381  0., osg::Vec3d(0, 0, 1)));
382  cantileverHolderBase->setScale(osg::Vec3d(.04 / poleDiameter, .04 / poleDiameter, sqrt(pow(holderWidth, 2.) + pow(extraHeight, 2.))));
383  cantileverHolderBase->addChild(poleBase);
384  rightPoleBase->addChild(cantileverHolderBase);
385  } else { // signal bridge
386  rightPoleScaleNode->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, poleMinHeight));
387  osg::PositionAttitudeTransform* leftPoleBase = new osg::PositionAttitudeTransform();
388  leftPoleBase->addChild(poleBase);
389  leftPoleBase->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, poleMinHeight));
390  osg::Vec3d leftPolePos = osg::Vec3d(0, -(horizontalWidth + 2. * poleOffset), 0.);
391  leftPoleBase->setPosition(leftPolePos);
392  rightPoleBase->addChild(leftPoleBase);
393  osg::PositionAttitudeTransform* bridgeBase = new osg::PositionAttitudeTransform();
394  bridgeBase->setPosition(osg::Vec3d(0., 0., poleMinHeight - .125));
395  bridgeBase->setAttitude(osg::Quat(DEG2RAD(90.), osg::Vec3d(1, 0, 0),
396  0., osg::Vec3d(0, 1, 0),
397  0., osg::Vec3d(0, 0, 1)));
398  bridgeBase->setScale(osg::Vec3d(.25 / poleDiameter, .25 / poleDiameter, leftPolePos.length()));
399  bridgeBase->addChild(poleBase);
400  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  for (std::advance(it, firstSignalLaneIx); it != appLanes.end(); it++) {
409  // get tlLinkIndices
410  const std::vector<MSLink*>& links = (*it)->getLinkCont();
411  std::set<int> tlIndices;
412  for (MSLink* link : links) {
413  if (link->getTLIndex() > -1) {
414  tlIndices.insert(link->getTLIndex());
415  }
416  }
417  std::set<int> seenTlIndices;
418  bool placeRepeaters = true;
419  for (MSLink* link : links) {
420  std::vector<std::pair<osg::Group*, osg::Vec3d>> signalTransforms = { {rightPoleBase, osg::Vec3d(0., 0., 0.)} };
421  if (placeRepeaters) {
422  signalTransforms.insert(signalTransforms.end(), repeaters.begin(), repeaters.end());
423  repeaters.clear();
424  placeRepeaters = false;
425  }
426  int tlIndex = link->getTLIndex();
427  if (tlIndex < 0 || seenTlIndices.find(tlIndex) != seenTlIndices.end()) {
428  continue;
429  }
430  for (const std::pair<osg::Group*, osg::Vec3d>& transform : signalTransforms) {
432  d.centerX = transform.second.x() + 0.15;
433  d.centerY = (onlyPedCycle) ? 0. : -(refPos + .5 * (*it)->getWidth() - ((double)tlIndices.size() / 2. - 1. + (double)seenTlIndices.size()) * 1.5 * tlWidth);
434  d.centerY += transform.second.y();
435  d.centerZ = (onlyPedCycle) ? 2.2 : 3.8;
436  d.centerZ += transform.second.z();
437  d.altitude = (onlyPedCycle) ? 0.6 : -1;
438  osg::PositionAttitudeTransform* tlNode = getTrafficLight(d, vars, links[0], tlg, tly, tlr, tlu, poleBase, false);
439  tlNode->setAttitude(osg::Quat(0., osg::Vec3d(1, 0, 0),
440  0., osg::Vec3d(0, 1, 0),
441  DEG2RAD(180.0), osg::Vec3d(0, 0, 1)));
442  transform.first->addChild(tlNode);
443  }
444  seenTlIndices.insert(tlIndex);
445  }
446  // only one signal for bike/pedestrian only edges
447  if (onlyPedCycle) {
448  break;
449  }
450  refPos += (*it)->getWidth();
451  }
452  // interaction
453  appBase->setNodeMask(1 << GUIOSGView::NODESET_TLSMODELS);
454  appBase->setName("tlLogic:" + tlLogic->getID());
455  addTo.addChild(appBase);
456  }
457  }
458 }
459 
460 
461 void
462 GUIOSGBuilder::buildDecal(const GUISUMOAbstractView::Decal& d, osg::Group& addTo) {
463  osg::Node* pLoadedModel = osgDB::readNodeFile(d.filename);
464  osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
465  double zOffset = 0.;
466  if (pLoadedModel == nullptr) {
467  // check for 2D image
468  osg::Image* pImage = osgDB::readImageFile(d.filename);
469  if (pImage == nullptr) {
470  base = nullptr;
471  WRITE_ERRORF(TL("Could not load '%'."), d.filename);
472  return;
473  }
474  osg::Texture2D* texture = new osg::Texture2D();
475  texture->setImage(pImage);
476  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  quad->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture);
478  osg::Geode* const pModel = new osg::Geode();
479  pModel->addDrawable(quad);
480  base->addChild(pModel);
481  zOffset = d.layer;
482  } else {
483  osg::ShadeModel* sm = new osg::ShadeModel();
484  sm->setMode(osg::ShadeModel::FLAT);
485  pLoadedModel->getOrCreateStateSet()->setAttribute(sm);
486  base->addChild(pLoadedModel);
487  }
488  osg::ComputeBoundsVisitor bboxCalc;
489  base->accept(bboxCalc);
490  const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
491  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  double xScale = d.width > 0 ? d.width / (bbox.xMax() - bbox.xMin()) : 1.;
493  double yScale = d.height > 0 ? d.height / (bbox.yMax() - bbox.yMin()) : 1.;
494  const double zScale = d.altitude > 0 ? d.altitude / (bbox.zMax() - bbox.zMin()) : 1.;
495  if (d.width < 0 && d.height < 0 && d.altitude > 0) {
496  xScale = yScale = zScale;
497  }
498  base->setScale(osg::Vec3d(xScale, yScale, zScale));
499  base->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ + zOffset));
500  base->setAttitude(osg::Quat(osg::DegreesToRadians(d.roll), osg::Vec3d(1, 0, 0),
501  osg::DegreesToRadians(d.tilt), osg::Vec3d(0, 1, 0),
502  osg::DegreesToRadians(d.rot), osg::Vec3d(0, 0, 1)));
503  addTo.addChild(base);
504 }
505 
506 
507 osg::PositionAttitudeTransform*
508 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  osg::PositionAttitudeTransform* ret = new osg::PositionAttitudeTransform();
510  double xScale = 1., yScale = 1., zScale = 1.;
511  if (tlg != nullptr) {
512  osg::ComputeBoundsVisitor bboxCalc;
513  tlg->accept(bboxCalc);
514  const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
515  xScale = d.width > 0 ? d.width / (bbox.xMax() - bbox.xMin()) : 1.;
516  yScale = d.height > 0 ? d.height / (bbox.yMax() - bbox.yMin()) : 1.;
517  double addHeight = (withPole) ? poleHeight : 0.;
518  zScale = d.altitude > 0 ? d.altitude / (addHeight + bbox.zMax() - bbox.zMin()) : 1.;
519  }
520  if (d.width < 0 && d.height < 0 && d.altitude > 0) {
521  xScale = yScale = zScale;
522  }
523  osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
524  osg::Switch* switchNode = new osg::Switch();
525  switchNode->addChild(createTrafficLightState(d, tlg, withPole, size, osg::Vec4d(0., 1., 0., transparency)));
526  switchNode->addChild(createTrafficLightState(d, tly, withPole, size, osg::Vec4d(1., 1., 0., transparency)));
527  switchNode->addChild(createTrafficLightState(d, tlr, withPole, size, osg::Vec4d(1., 0., 0., transparency)));
528  switchNode->addChild(createTrafficLightState(d, tlu, withPole, size, osg::Vec4d(1., .5, 0., transparency)));
529  base->addChild(switchNode);
530  vars.addSwitchCommand(new GUIOSGView::Command_TLSChange(link, switchNode));
531  if (withPole) {
532  base->setPosition(osg::Vec3d(0., 0., poleHeight));
533  osg::PositionAttitudeTransform* poleBase = new osg::PositionAttitudeTransform();
534  poleBase->addChild(pole);
535  poleBase->setScale(osg::Vec3d(1., 1., poleHeight));
536  ret->addChild(poleBase);
537  }
538  ret->setAttitude(osg::Quat(osg::DegreesToRadians(d.roll), osg::Vec3(1, 0, 0),
539  osg::DegreesToRadians(d.tilt), osg::Vec3(0, 1, 0),
540  osg::DegreesToRadians(d.rot), osg::Vec3(0, 0, 1)));
541  ret->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ));
542  ret->setScale(osg::Vec3d(xScale, yScale, zScale));
543  ret->addChild(base);
544  return ret;
545 }
546 
547 
548 osg::PositionAttitudeTransform*
549 GUIOSGBuilder::createTrafficLightState(const GUISUMOAbstractView::Decal& d, osg::Node* tl, const double withPole, const double size, osg::Vec4d color) {
550  osg::PositionAttitudeTransform* ret = new osg::PositionAttitudeTransform();
551  if (tl != nullptr) {
552  ret->addChild(tl);
553  }
554  if (size > 0.) {
555  unsigned int nodeMask = (withPole) ? 1 << GUIOSGView::NodeSetGroup::NODESET_TLSDOMES : 1 << GUIOSGView::NodeSetGroup::NODESET_TLSLINKMARKERS;
556  osg::Geode* geode = new osg::Geode();
557  osg::Vec3d center = osg::Vec3d(0., 0., (withPole) ? -1.8 : 0.);
558  osg::ShapeDrawable* shape = new osg::ShapeDrawable(new osg::Sphere(center, (float)size));
559  geode->addDrawable(shape);
560  osg::ref_ptr<osg::StateSet> ss = shape->getOrCreateStateSet();
561  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
562  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
563  shape->setColor(color);
564  osg::PositionAttitudeTransform* ellipse = new osg::PositionAttitudeTransform();
565  ellipse->addChild(geode);
566  ellipse->setPosition(center);
567  ellipse->setPivotPoint(center);
568  if (withPole) {
569  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  ret->addChild(ellipse);
575  }
576  return ret;
577 }
578 
579 
580 void
581 GUIOSGBuilder::setShapeState(osg::ref_ptr<osg::ShapeDrawable> shape) {
582  osg::ref_ptr<osg::StateSet> ss = shape->getOrCreateStateSet();
583  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
584  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
585 }
586 
587 
588 GUIOSGView::OSGMovable
589 GUIOSGBuilder::buildMovable(const MSVehicleType& type) {
590  GUIOSGView::OSGMovable m;
591  m.pos = new osg::PositionAttitudeTransform();
592  double enlarge = 0.05;
593  const std::string& osgFile = type.getOSGFile();
594  if (myCars.find(osgFile) == myCars.end()) {
595  myCars[osgFile] = osgDB::readNodeFile(osgFile);
596  if (myCars[osgFile] == 0) {
597  WRITE_ERRORF(TL("Could not load '%'. The model is replaced by a cone shape."), osgFile);
598  osg::PositionAttitudeTransform* rot = new osg::PositionAttitudeTransform();
599  rot->addChild(new osg::ShapeDrawable(new osg::Cone(osg::Vec3d(0, 0, 0), 1.0f, 1.0f)));
600  rot->setAttitude(osg::Quat(osg::DegreesToRadians(90.), osg::Vec3(1, 0, 0),
601  0., osg::Vec3(0, 1, 0),
602  0., osg::Vec3(0, 0, 1)));
603  myCars[osgFile] = rot;
604  }
605  }
606  osg::Node* carNode = myCars[osgFile];
607  if (carNode != nullptr) {
608  osg::ComputeBoundsVisitor bboxCalc;
609  carNode->accept(bboxCalc);
610  const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
611  osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
612  base->addChild(carNode);
613  base->setPivotPoint(osg::Vec3d((bbox.xMin() + bbox.xMax()) / 2., bbox.yMin(), bbox.zMin()));
614  base->setScale(osg::Vec3d(type.getWidth() / (bbox.xMax() - bbox.xMin()),
615  type.getLength() / (bbox.yMax() - bbox.yMin()),
616  type.getHeight() / (bbox.zMax() - bbox.zMin())));
617  m.pos->addChild(base);
618 
619  // material for coloring the person or vehicle body
620  m.mat = new osg::Material();
621  osg::ref_ptr<osg::StateSet> ss = base->getOrCreateStateSet();
622  ss->setAttribute(m.mat, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
623  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
624  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
625  }
626  if (type.getVehicleClass() != SVC_PEDESTRIAN) {
627  m.lights = new osg::Switch();
628  for (double sideFactor = -1.; sideFactor < 2.5; sideFactor += 2.) {
629  osg::Geode* geode = new osg::Geode();
630  osg::ShapeDrawable* right = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3d((type.getWidth() / 2. + enlarge)*sideFactor, 0., type.getHeight() / 2.), 0.2f));
631  geode->addDrawable(right);
632  //pat->addChild(geode);
633  setShapeState(right);
634  right->setColor(osg::Vec4(1.f, .5f, 0.f, .8f));
635  osg::Sequence* seq = new osg::Sequence();
636  // Wikipedia says about 1.5Hz
637  seq->addChild(geode, .33);
638  seq->addChild(new osg::Geode(), .33);
639  // loop through all children
640  seq->setInterval(osg::Sequence::LOOP, 0, -1);
641  // real-time playback, repeat indefinitely
642  seq->setDuration(1.0f, -1);
643  // must be started explicitly
644  seq->setMode(osg::Sequence::START);
645  m.lights->addChild(seq);
646  }
647  osg::Geode* geode = new osg::Geode();
648  osg::CompositeShape* comp = new osg::CompositeShape();
649  comp->addChild(new osg::Sphere(osg::Vec3d(-(type.getWidth() / 2. + enlarge), type.getLength() + enlarge, type.getHeight() / 2.), .2f));
650  comp->addChild(new osg::Sphere(osg::Vec3d(type.getWidth() / 2. + enlarge, type.getLength() + enlarge, type.getHeight() / 2.), .2f));
651  osg::ShapeDrawable* brake = new osg::ShapeDrawable(comp);
652  brake->setColor(osg::Vec4(1.f, 0.f, 0.f, .8f));
653  geode->addDrawable(brake);
654  setShapeState(brake);
655  m.lights->addChild(geode);
656 
657  osg::Vec3d center(0, -type.getLength() / 2., 0.);
658  osg::PositionAttitudeTransform* ellipse = new osg::PositionAttitudeTransform();
659  ellipse->addChild(geode);
660  ellipse->addChild(m.lights);
661  ellipse->setPivotPoint(center);
662  ellipse->setPosition(center);
663  m.pos->addChild(ellipse);
664  }
665  m.active = true;
666  return m;
667 }
668 
669 
670 osg::Node*
671 GUIOSGBuilder::buildPlane(const float length) {
672  osg::Geode* geode = new osg::Geode();
673  osg::Geometry* geom = new osg::Geometry;
674  geode->addDrawable(geom);
675  osg::Vec3Array* coords = new osg::Vec3Array(4); // OSG needs float coordinates here
676  geom->setVertexArray(coords);
677  (*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  osg::Vec3Array* normals = new osg::Vec3Array(1); // OSG needs float coordinates here
682  (*normals)[0].set(0, 0, 1);
683  geom->setNormalArray(normals, osg::Array::BIND_PER_PRIMITIVE_SET);
684  osg::Vec4ubArray* colors = new osg::Vec4ubArray(1);
685  (*colors)[0].set(0, 255, 0, 255);
686  geom->setColorArray(colors, osg::Array::BIND_OVERALL);
687  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 4));
688 
689  osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
690  ss->setRenderingHint(osg::StateSet::OPAQUE_BIN);
691  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
692 
693  return geode;
694 }
695 
696 
697 #endif
698 
699 /****************************************************************************/
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define WRITE_MESSAGEF(...)
Definition: MsgHandler.h:298
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define TL(string)
Definition: MsgHandler.h:315
bool isForbidden(SVCPermissions permissions)
Returns whether an edge with the given permission is a forbidden edge.
bool isSidewalk(SVCPermissions permissions)
Returns whether an edge with the given permission is a sidewalk.
bool noVehicles(SVCPermissions permissions)
Returns whether an edge with the given permission forbids vehicles.
bool isBikepath(SVCPermissions permissions)
Returns whether an edge with the given permission is a bicycle edge.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_PEDESTRIAN
pedestrian
T MIN2(T a, T b)
Definition: StdDefs.h:76
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
const std::string & getMicrosimID() const
Returns the id of the object as known to microsim.
Definition: GUIGlObject.h:143
const MSJunction & getJunction() const
Returns the represented junction.
Representation of a lane in the micro simulation (gui-version)
Definition: GUILane.h:60
A MSNet extended by some values for usage within the gui.
Definition: GUINet.h:82
std::vector< GUIJunctionWrapper * > myJunctionWrapper
Wrapped MS-junctions.
Definition: GUINet.h:387
const MSEdgeVector & getEdges() const
Returns loaded edges.
A road/street connecting two junctions.
Definition: MSEdge.h:77
bool isCrossing() const
return whether this edge is a pedestrian crossing
Definition: MSEdge.h:270
SVCPermissions getPermissions() const
Returns the combined permissions of all lanes of this edge.
Definition: MSEdge.h:626
bool isWalkingArea() const
return whether this edge is walking area
Definition: MSEdge.h:284
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:168
double getLength() const
return the length of the edge
Definition: MSEdge.h:662
double getWidth() const
Returns the edges's width (sum over all lanes)
Definition: MSEdge.h:633
const PositionVector & getShape() const
Returns this junction's shape.
Definition: MSJunction.h:91
Representation of a lane in the micro simulation.
Definition: MSLane.h:84
SVCPermissions getPermissions() const
Returns the vehicle class permissions for this lane.
Definition: MSLane.h:606
double getWidth() const
Returns the lane's width.
Definition: MSLane.h:627
virtual const PositionVector & getShape(bool) const
Definition: MSLane.h:294
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:182
MSTLLogicControl & getTLSControl()
Returns the tls logics control.
Definition: MSNet.h:451
MSEdgeControl & getEdgeControl()
Returns the edge control.
Definition: MSNet.h:421
Storage for all programs of a single tls.
void addSwitchCommand(OnSwitchAction *c)
MSTrafficLightLogic * getActive() const
std::vector< std::string > getAllTLIds() const
TLSLogicVariants & get(const std::string &id) const
Returns the variants of a named tls.
The parent class for traffic light logics.
const LinkVector & getLinksAt(int i) const
Returns the list of links that are controlled by the signals at the given position.
std::vector< LaneVector > LaneVectorVector
Definition of a list that holds lists of lanes that do have the same attribute.
const LaneVectorVector & getLaneVectors() const
Returns the list of lists of all lanes controlled by this tls.
const LinkVectorVector & getLinks() const
Returns the list of lists of all affected links.
std::vector< LinkVector > LinkVectorVector
Definition of a list that holds lists of links that do have the same attribute.
std::vector< MSLink * > LinkVector
Definition of the list of links that are subjected to this tls.
The car-following model and parameter.
Definition: MSVehicleType.h:63
double getWidth() const
Get the width which vehicles of this class shall have when being drawn.
SUMOVehicleClass getVehicleClass() const
Get this vehicle type's vehicle class.
std::string getOSGFile() const
Get this vehicle type's 3D model file name.
double getHeight() const
Get the height which vehicles of this class shall have when being drawn.
double getLength() const
Get vehicle's length [m].
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double x() const
Returns the x-position.
Definition: Position.h:55
double z() const
Returns the z-position.
Definition: Position.h:65
double y() const
Returns the y-position.
Definition: Position.h:60
A list of positions.
double rotationDegreeAtOffset(double pos) const
Returns the rotation at the given length.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
A decal (an image) that can be shown.
double tilt
The tilt of the image to the ground plane (in degrees)
double centerX
The center of the image in x-direction (net coordinates, in m)
double height
The height of the image (net coordinates in y-direction, in m)
double width
The width of the image (net coordinates in x-direction, in m)
double rot
The rotation of the image in the ground plane (in degrees)
double layer
The layer of the image.
double altitude
The altitude of the image (net coordinates in z-direction, in m)
double centerY
The center of the image in y-direction (net coordinates, in m)
double centerZ
The center of the image in z-direction (net coordinates, in m)
std::string filename
The path to the file the image is located at.
double roll
The roll of the image to the ground plane (in degrees)