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.org/sumo
3 // Copyright (C) 2001-2022 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 /****************************************************************************/
19 // Builds OSG nodes from microsim objects
20 /****************************************************************************/
21 #include <config.h>
22 
23 #ifdef HAVE_OSG
24 
25 #include <guisim/GUIEdge.h>
27 #include <guisim/GUILane.h>
28 #include <guisim/GUINet.h>
29 #include <microsim/MSEdge.h>
30 #include <microsim/MSEdgeControl.h>
31 #include <microsim/MSJunction.h>
33 #include <microsim/MSLane.h>
34 #include <microsim/MSNet.h>
35 #include <microsim/MSVehicleType.h>
40 
41 #include "GUIOSGView.h"
42 #include "GUIOSGBuilder.h"
43 
44 //#define DEBUG_TESSEL
45 
46 // ===========================================================================
47 // static member variables
48 // ===========================================================================
49 
50 std::map<std::string, osg::ref_ptr<osg::Node> > GUIOSGBuilder::myCars;
51 
52 // ===========================================================================
53 // member method definitions
54 // ===========================================================================
55 
56 osg::Group*
57 GUIOSGBuilder::buildOSGScene(osg::Node* const tlg, osg::Node* const tly, osg::Node* const tlr, osg::Node* const tlu) {
58  osgUtil::Tessellator tesselator;
59  osg::Group* root = new osg::Group();
60  GUINet* net = static_cast<GUINet*>(MSNet::getInstance());
61  // build edges
62  for (const MSEdge* e : net->getEdgeControl().getEdges()) {
63  if (!e->isInternal()) {
64  buildOSGEdgeGeometry(*e, *root, tesselator);
65  }
66  }
67  // build junctions
68  for (int index = 0; index < (int)net->myJunctionWrapper.size(); ++index) {
69  buildOSGJunctionGeometry(*net->myJunctionWrapper[index], *root, tesselator);
70  }
71  // build traffic lights
73  const std::vector<std::string> tlids = net->getTLSControl().getAllTLIds();
74  for (std::vector<std::string>::const_iterator i = tlids.begin(); i != tlids.end(); ++i) {
77  const MSLane* lastLane = 0;
78  int idx = 0;
79  for (MSTrafficLightLogic::LaneVectorVector::const_iterator j = lanes.begin(); j != lanes.end(); ++j, ++idx) {
80  if ((*j).size() == 0) {
81  continue;
82  }
83  const MSLane* const lane = (*j)[0];
84  const Position pos = lane->getShape().back();
85  const double angle = osg::DegreesToRadians(lane->getShape().rotationDegreeAtOffset(-1.) + 90.);
86  d.centerZ = pos.z() + 4.;
87  if (lane == lastLane) {
88  d.centerX += 1.2 * sin(angle);
89  d.centerY += 1.2 * cos(angle);
90  } else {
91  d.centerX = pos.x() - 1.5 * sin(angle);
92  d.centerY = pos.y() - 1.5 * cos(angle);
93  }
94  osg::Switch* switchNode = new osg::Switch();
95  switchNode->addChild(getTrafficLight(d, tlg, osg::Vec4d(0.1, 0.5, 0.1, 1.0), .25), false);
96  switchNode->addChild(getTrafficLight(d, tly, osg::Vec4d(0.5, 0.5, 0.1, 1.0), .25), false);
97  switchNode->addChild(getTrafficLight(d, tlr, osg::Vec4d(0.5, 0.1, 0.1, 1.0), .25), false);
98  switchNode->addChild(getTrafficLight(d, tlu, osg::Vec4d(0.8, 0.4, 0.0, 1.0), .25), false);
99  root->addChild(switchNode);
100  const MSLink* const l = vars.getActive()->getLinksAt(idx)[0];
101  vars.addSwitchCommand(new GUIOSGView::Command_TLSChange(l, switchNode));
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 PositionVector& shape = l->getShape();
140  osg::Geode* geode = new osg::Geode();
141  osg::Geometry* geom = new osg::Geometry();
142  geode->addDrawable(geom);
143  addTo.addChild(geode);
144  const int shapeSize = (int)(edge.isWalkingArea() ? shape.size() : shape.size() * 2);
145  const float zOffset = edge.isWalkingArea() || edge.isCrossing() ? 0.01f : 0.f;
146  osg::Vec3Array* osg_coords = new osg::Vec3Array(shapeSize);
147  geom->setVertexArray(osg_coords);
148  if (edge.isWalkingArea()) {
149  int index = 0;
150  for (int k = 0; k < (int)shape.size(); ++k, ++index) {
151  (*osg_coords)[index].set((float)shape[k].x(), (float)shape[k].y(), (float)shape[k].z() + zOffset);
152  }
153  } else {
154  PositionVector rshape = shape;
155  rshape.move2side(l->getWidth() / 2);
156  int index = 0;
157  for (int k = 0; k < (int)rshape.size(); ++k, ++index) {
158  (*osg_coords)[index].set((float)rshape[k].x(), (float)rshape[k].y(), (float)rshape[k].z() + zOffset);
159  }
160  PositionVector lshape = shape;
161  lshape.move2side(-l->getWidth() / 2);
162  for (int k = (int) lshape.size() - 1; k >= 0; --k, ++index) {
163  (*osg_coords)[index].set((float)lshape[k].x(), (float)lshape[k].y(), (float)lshape[k].z() + zOffset);
164  }
165  }
166  osg::Vec3Array* osg_normals = new osg::Vec3Array(1);
167  (*osg_normals)[0] = osg::Vec3(0, 0, 1);
168 #if OSG_MIN_VERSION_REQUIRED(3,2,0)
169  geom->setNormalArray(osg_normals, osg::Array::BIND_PER_PRIMITIVE_SET);
170 #else
171  geom->setNormalArray(osg_normals);
172  geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
173 #endif
174  osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
175  (*osg_colors)[0].set(128, 128, 128, 255);
176 #if OSG_MIN_VERSION_REQUIRED(3,2,0)
177  geom->setColorArray(osg_colors, osg::Array::BIND_OVERALL);
178 #else
179  geom->setColorArray(osg_colors);
180  geom->setColorBinding(osg::Geometry::BIND_OVERALL);
181 #endif
182  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, shapeSize));
183 
184  osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
185  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
186  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
187 
188  if (shape.size() > 2) {
189  tessellator.retessellatePolygons(*geom);
190 #ifdef DEBUG_TESSEL
191  std::cout << "l=" << l->getID() << " origPoints=" << shape.size() << " geomSize=" << geom->getVertexArray()->getNumElements() << " points=";
192  for (int i = 0; i < (int)geom->getVertexArray()->getNumElements(); i++) {
193  const osg::Vec3& p = (*((osg::Vec3Array*)geom->getVertexArray()))[i];
194  std::cout << p.x() << "," << p.y() << "," << p.z() << " ";
195  }
196  std::cout << "\n";
197 #endif
198  }
199  static_cast<GUILane*>(l)->setGeometry(geom);
200  }
201 }
202 
203 
204 void
205 GUIOSGBuilder::buildOSGJunctionGeometry(GUIJunctionWrapper& junction,
206  osg::Group& addTo,
207  osgUtil::Tessellator& tessellator) {
208  const PositionVector& shape = junction.getJunction().getShape();
209  osg::Geode* geode = new osg::Geode();
210  osg::Geometry* geom = new osg::Geometry();
211  geode->addDrawable(geom);
212  addTo.addChild(geode);
213  osg::Vec3Array* osg_coords = new osg::Vec3Array((int)shape.size());
214  geom->setVertexArray(osg_coords);
215  for (int k = 0; k < (int)shape.size(); ++k) {
216  (*osg_coords)[k].set((float)shape[k].x(), (float)shape[k].y(), (float)shape[k].z());
217  }
218  osg::Vec3Array* osg_normals = new osg::Vec3Array(1);
219  (*osg_normals)[0] = osg::Vec3(0, 0, 1);
220 #if OSG_MIN_VERSION_REQUIRED(3,2,0)
221  geom->setNormalArray(osg_normals, osg::Array::BIND_PER_PRIMITIVE_SET);
222 #else
223  geom->setNormalArray(osg_normals);
224  geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
225 #endif
226  osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
227  (*osg_colors)[0].set(128, 128, 128, 255);
228 #if OSG_MIN_VERSION_REQUIRED(3,2,0)
229  geom->setColorArray(osg_colors, osg::Array::BIND_OVERALL);
230 #else
231  geom->setColorArray(osg_colors);
232  geom->setColorBinding(osg::Geometry::BIND_OVERALL);
233 #endif
234  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, (int)shape.size()));
235 
236  osg::ref_ptr<osg::StateSet> ss = geode->getOrCreateStateSet();
237  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
238  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
239 
240  if (shape.size() > 4) {
241  tessellator.retessellatePolygons(*geom);
242  }
243  junction.setGeometry(geom);
244 }
245 
246 
247 void
248 GUIOSGBuilder::buildDecal(const GUISUMOAbstractView::Decal& d, osg::Group& addTo) {
249  osg::Node* pLoadedModel = osgDB::readNodeFile(d.filename);
250  if (pLoadedModel == nullptr) {
251  WRITE_ERROR("Could not load '" + d.filename + "'.");
252  return;
253  }
254  osg::ShadeModel* sm = new osg::ShadeModel();
255  sm->setMode(osg::ShadeModel::FLAT);
256  pLoadedModel->getOrCreateStateSet()->setAttribute(sm);
257  osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
258  base->addChild(pLoadedModel);
259  osg::ComputeBoundsVisitor bboxCalc;
260  pLoadedModel->accept(bboxCalc);
261  const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
262  WRITE_MESSAGE("Loaded decal '" + d.filename + "' with bounding box " + toString(Position(bbox.xMin(), bbox.yMin(), bbox.zMin())) + " " + toString(Position(bbox.xMax(), bbox.yMax(), bbox.zMax())) + ".");
263  double xScale = d.width > 0 ? d.width / (bbox.xMax() - bbox.xMin()) : 1.;
264  double yScale = d.height > 0 ? d.height / (bbox.yMax() - bbox.yMin()) : 1.;
265  const double zScale = d.altitude > 0 ? d.altitude / (bbox.zMax() - bbox.zMin()) : 1.;
266  if (d.width < 0 && d.height < 0 && d.altitude > 0) {
267  xScale = yScale = zScale;
268  }
269  base->setScale(osg::Vec3d(xScale, yScale, zScale));
270  base->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ));
271  base->setAttitude(osg::Quat(osg::DegreesToRadians(d.roll), osg::Vec3d(1, 0, 0),
272  osg::DegreesToRadians(d.tilt), osg::Vec3d(0, 1, 0),
273  osg::DegreesToRadians(d.rot), osg::Vec3d(0, 0, 1)));
274  addTo.addChild(base);
275 }
276 
277 
278 osg::PositionAttitudeTransform*
279 GUIOSGBuilder::getTrafficLight(const GUISUMOAbstractView::Decal& d, osg::Node* tl, const osg::Vec4& color, const double size) {
280  osg::PositionAttitudeTransform* ret = new osg::PositionAttitudeTransform();
281  if (tl != nullptr) {
282  osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
283  base->addChild(tl);
284  osg::ComputeBoundsVisitor bboxCalc;
285  tl->accept(bboxCalc);
286  const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
287  double xScale = d.width > 0 ? d.width / (bbox.xMax() - bbox.xMin()) : 1.;
288  double yScale = d.height > 0 ? d.height / (bbox.yMax() - bbox.yMin()) : 1.;
289  const double zScale = d.altitude > 0 ? d.altitude / (bbox.zMax() - bbox.zMin()) : 1.;
290  if (d.width < 0 && d.height < 0 && d.altitude > 0) {
291  xScale = yScale = zScale;
292  }
293  base->setScale(osg::Vec3d(xScale, yScale, zScale));
294  base->setPosition(osg::Vec3d(d.centerX, d.centerY, d.centerZ));
295  base->setAttitude(osg::Quat(osg::DegreesToRadians(d.roll), osg::Vec3(1, 0, 0),
296  osg::DegreesToRadians(d.tilt), osg::Vec3(0, 1, 0),
297  osg::DegreesToRadians(d.rot), osg::Vec3(0, 0, 1)));
298  ret->addChild(base);
299  }
300  osg::Geode* geode = new osg::Geode();
301  osg::Vec3d center(d.centerX, d.centerY, d.centerZ);
302  osg::ShapeDrawable* shape = new osg::ShapeDrawable(new osg::Sphere(center, (float)size));
303  geode->addDrawable(shape);
304  osg::ref_ptr<osg::StateSet> ss = shape->getOrCreateStateSet();
305  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
306  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
307  osg::PositionAttitudeTransform* ellipse = new osg::PositionAttitudeTransform();
308  ellipse->addChild(geode);
309  ellipse->setPivotPoint(center);
310  ellipse->setPosition(center);
311  ellipse->setScale(osg::Vec3d(4., 4., 2.5 * d.altitude + 1.1));
312  shape->setColor(color);
313  ret->addChild(ellipse);
314  return ret;
315 }
316 
317 
318 void
319 GUIOSGBuilder::setShapeState(osg::ref_ptr<osg::ShapeDrawable> shape) {
320  osg::ref_ptr<osg::StateSet> ss = shape->getOrCreateStateSet();
321  ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
322  ss->setMode(GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
323 }
324 
325 
326 GUIOSGView::OSGMovable
327 GUIOSGBuilder::buildMovable(const MSVehicleType& type) {
328  GUIOSGView::OSGMovable m;
329  m.pos = new osg::PositionAttitudeTransform();
330  double enlarge = 0.;
331  const std::string& osgFile = type.getOSGFile();
332  if (myCars.find(osgFile) == myCars.end()) {
333  myCars[osgFile] = osgDB::readNodeFile(osgFile);
334  if (myCars[osgFile] == 0) {
335  WRITE_ERROR("Could not load '" + osgFile + "'.");
336  }
337  }
338  osg::Node* carNode = myCars[osgFile];
339  if (carNode != nullptr) {
340  osg::ComputeBoundsVisitor bboxCalc;
341  carNode->accept(bboxCalc);
342  const osg::BoundingBox& bbox = bboxCalc.getBoundingBox();
343  osg::PositionAttitudeTransform* base = new osg::PositionAttitudeTransform();
344  base->addChild(carNode);
345  base->setPivotPoint(osg::Vec3d((bbox.xMin() + bbox.xMax()) / 2., bbox.yMin(), bbox.zMin()));
346  base->setScale(osg::Vec3d(type.getWidth() / (bbox.xMax() - bbox.xMin()),
347  type.getLength() / (bbox.yMax() - bbox.yMin()),
348  type.getHeight() / (bbox.zMax() - bbox.zMin())));
349  m.pos->addChild(base);
350  enlarge = type.getMinGap() / 2.;
351  }
352  m.lights = new osg::Switch();
353  for (double offset = -0.3; offset < 0.5; offset += 0.6) {
354  osg::Geode* geode = new osg::Geode();
355  osg::ShapeDrawable* right = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3d(offset, (type.getLength() - .9) / 2., (type.getHeight() - .5) / 2.), .1f));
356  geode->addDrawable(right);
357  setShapeState(right);
358  right->setColor(osg::Vec4(1.f, .5f, 0.f, .8f));
359  osg::Sequence* seq = new osg::Sequence();
360  // Wikipedia says about 1.5Hz
361  seq->addChild(geode, .33);
362  seq->addChild(new osg::Geode(), .33);
363  // loop through all children
364  seq->setInterval(osg::Sequence::LOOP, 0, -1);
365  // real-time playback, repeat indefinitely
366  seq->setDuration(1.0f, -1);
367  // must be started explicitly
368  seq->setMode(osg::Sequence::START);
369  m.lights->addChild(seq);
370  }
371 
372  osg::Geode* geode = new osg::Geode();
373  osg::CompositeShape* comp = new osg::CompositeShape();
374  comp->addChild(new osg::Sphere(osg::Vec3d(-0.3, (type.getLength() + .8) / 2., (type.getHeight() - .5) / 2.), .1f));
375  comp->addChild(new osg::Sphere(osg::Vec3d(0.3, (type.getLength() + .8) / 2., (type.getHeight() - .5) / 2.), .1f));
376  osg::ShapeDrawable* brake = new osg::ShapeDrawable(comp);
377  brake->setColor(osg::Vec4(1.f, 0.f, 0.f, .8f));
378  geode->addDrawable(brake);
379  setShapeState(brake);
380  m.lights->addChild(geode);
381 
382  geode = new osg::Geode();
383  osg::Vec3d center(0, type.getLength() / 2., type.getHeight() / 2.);
384  m.geom = new osg::ShapeDrawable(new osg::Sphere(center, .5f));
385  geode->addDrawable(m.geom);
386  setShapeState(m.geom);
387  osg::PositionAttitudeTransform* ellipse = new osg::PositionAttitudeTransform();
388  ellipse->addChild(geode);
389  ellipse->addChild(m.lights);
390  ellipse->setPivotPoint(center);
391  ellipse->setPosition(center);
392  ellipse->setScale(osg::Vec3d(type.getWidth() + enlarge, type.getLength() + enlarge, type.getHeight() + enlarge));
393  m.pos->addChild(ellipse);
394  m.active = true;
395  return m;
396 }
397 
398 #endif
399 
400 /****************************************************************************/
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:282
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:288
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
const MSJunction & getJunction() const
Returns the represented junction.
Representation of a lane in the micro simulation (gui-version)
Definition: GUILane.h:59
A MSNet extended by some values for usage within the gui.
Definition: GUINet.h:81
std::vector< GUIJunctionWrapper * > myJunctionWrapper
Wrapped MS-junctions.
Definition: GUINet.h:365
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:267
bool isWalkingArea() const
return whether this edge is walking area
Definition: MSEdge.h:281
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:168
const PositionVector & getShape() const
Returns this junction's shape.
Definition: MSJunction.h:88
Representation of a lane in the micro simulation.
Definition: MSLane.h:82
const PositionVector & getShape() const
Returns this lane's shape.
Definition: MSLane.h:478
double getWidth() const
Returns the lane's width.
Definition: MSLane.h:556
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:174
MSTLLogicControl & getTLSControl()
Returns the tls logics control.
Definition: MSNet.h:449
MSEdgeControl & getEdgeControl()
Returns the edge control.
Definition: MSNet.h:419
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.
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.
The car-following model and parameter.
Definition: MSVehicleType.h:62
double getWidth() const
Get the width which vehicles of this class shall have when being drawn.
std::string getOSGFile() const
Get this vehicle type's 3D model file name.
double getMinGap() const
Get the free space in front of vehicles of this class.
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 ammount
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 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)