1
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
3
* This library is open source and may be redistributed and/or modified under
4
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5
* (at your option) any later version. The full license is in LICENSE file
6
* included with this distribution, and on the openscenegraph.org website.
8
* This library is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* OpenSceneGraph Public License for more details.
20
#include <osg/io_utils>
21
#include <osg/NodeVisitor>
23
#include <osg/MatrixTransform>
24
#include <osgViewer/Renderer>
25
#include <osgAnimation/StatsHandler>
26
#include <osgAnimation/EaseMotion>
27
#include <osgAnimation/StatsVisitor>
28
#include <osgViewer/ViewerEventHandlers>
29
#include <osgViewer/Renderer>
30
#include <osgAnimation/TimelineAnimationManager>
32
#include <osg/PolygonMode>
33
#include <osg/Geometry>
37
static unsigned int getRandomValueinRange(unsigned int v)
39
return static_cast<unsigned int>((rand() * 1.0 * v)/(RAND_MAX-1));
43
namespace osgAnimation
47
osg::Geometry* createBackgroundRectangle(const osg::Vec3& pos, const float width, const float height, osg::Vec4& color)
49
osg::StateSet *ss = new osg::StateSet;
50
osg::Geometry* geometry = new osg::Geometry;
52
geometry->setUseDisplayList(false);
53
geometry->setStateSet(ss);
55
osg::Vec3Array* vertices = new osg::Vec3Array;
56
geometry->setVertexArray(vertices);
58
vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0));
59
vertices->push_back(osg::Vec3(pos.x(), pos.y()-height,0));
60
vertices->push_back(osg::Vec3(pos.x()+width, pos.y()-height,0));
61
vertices->push_back(osg::Vec3(pos.x()+width, pos.y(),0));
63
osg::Vec4Array* colors = new osg::Vec4Array;
64
colors->push_back(color);
65
geometry->setColorArray(colors);
66
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
68
osg::DrawElementsUInt *base = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS,0);
74
geometry->addPrimitiveSet(base);
79
struct StatsGraph : public osg::MatrixTransform
81
StatsGraph(osg::Vec3 pos, float width, float height)
82
: _pos(pos), _width(width), _height(height),
83
_statsGraphGeode(new osg::Geode)
85
_pos = pos - osg::Vec3(0, _height, 0.1);
86
setMatrix(osg::Matrix::translate(_pos));
87
setDataVariance(osg::Object::DYNAMIC);
88
addChild(_statsGraphGeode.get());
89
_statsGraphGeode->setCullingActive(false);
92
void changeYposition(float y)
94
osg::Vec3 _pos = getMatrix().getTrans();
95
_pos[1] = y - _height;
96
setMatrix(osg::Matrix::translate(_pos));
99
void addStatGraph(osg::Stats* viewerStats, osg::Stats* stats, const osg::Vec4& color, float max, const std::string& nameBegin, const std::string& nameEnd = "")
101
_statsGraphGeode->addDrawable(new Graph(_width, _height, viewerStats, stats, color, max, nameBegin, nameEnd));
108
osg::ref_ptr<osg::Geode> _statsGraphGeode;
110
struct NeverCull : public osg::Drawable::CullCallback
113
bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::RenderInfo* renderInfo) const { return false;}
117
struct Graph : public osg::Geometry
119
Graph(float width, float height, osg::Stats* viewerStats, osg::Stats* stats,
120
const osg::Vec4& color, float max, const std::string& nameBegin, const std::string& nameEnd = "")
122
setDataVariance(osg::Object::DYNAMIC);
123
setUseDisplayList(false);
125
setVertexArray(new osg::Vec3Array);
126
getVertexArray()->setDataVariance(osg::Object::DYNAMIC);
129
setUpdateCallback(new GraphUpdateCallback(width, height, viewerStats, stats, max, nameBegin, nameEnd));
130
setCullCallback(new NeverCull);
133
void setColor(const osg::Vec4& color) {
134
osg::Vec4Array* colors = new osg::Vec4Array;
135
colors->push_back(color);
136
setColorArray(colors);
137
setColorBinding(osg::Geometry::BIND_OVERALL);
142
struct GraphUpdateCallback : public osg::Drawable::UpdateCallback
145
const unsigned int _width;
146
const unsigned int _height;
147
mutable unsigned int _curX;
148
osg::Stats* _viewerStats;
151
const std::string _nameBegin;
152
const std::string _nameEnd;
153
mutable int _frameNumber;
155
GraphUpdateCallback(float width, float height, osg::Stats* viewerStats, osg::Stats* stats,
156
float max, const std::string& nameBegin, const std::string& nameEnd = "")
157
: _width((unsigned int)width), _height((unsigned int)height), _curX(0),
158
_viewerStats(viewerStats), _stats(stats), _max(max), _nameBegin(nameBegin), _nameEnd(nameEnd), _frameNumber(0)
161
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable)
163
if (nv->getVisitorType() != osg::NodeVisitor::UPDATE_VISITOR)
166
osg::Geometry* geometry = const_cast<osg::Geometry*>(drawable->asGeometry());
167
if (!geometry) return;
168
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
169
if (!vertices) return;
171
int frameNumber = nv->getFrameStamp()->getFrameNumber();
172
if (frameNumber == _frameNumber)
178
if (_nameEnd.empty())
180
if (!_stats->getAttribute(_stats->getLatestFrameNumber(), _nameBegin, value ))
187
double beginValue, endValue;
188
if (_stats->getAttribute( frameNumber, _nameBegin, beginValue) &&
189
_stats->getAttribute( frameNumber, _nameEnd, endValue) )
191
value = endValue - beginValue;
198
// Add new vertex for this frame.
199
value = osg::clampTo(value, 0.0, double(_max));
201
if (!vertices->size()) {
202
for (int i = 0; i < (int)_width; i++)
203
vertices->push_back(osg::Vec3(float(_curX++), 0, 0));
204
// Create primitive set if none exists.
205
if (geometry->getNumPrimitiveSets() == 0)
206
geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, 0));
207
osg::DrawArrays* drawArrays = dynamic_cast<osg::DrawArrays*>(geometry->getPrimitiveSet(0));
208
drawArrays->setFirst(0);
209
drawArrays->setCount(vertices->size());
211
vertices->push_back(osg::Vec3(float(_curX), float(_height) / _max * value, 0));
213
unsigned int excedent = vertices->size() - _width;
214
vertices->erase(vertices->begin(), vertices->begin() + excedent);
216
// Make the graph scroll when there is enough data.
217
// Note: We check the frame number so that even if we have
218
// many graphs, the transform is translated only once per
220
static const float increment = -1.0;
221
if (_frameNumber != frameNumber)
223
// We know the exact layout of this part of the scene
224
// graph, so this is OK...
225
osg::MatrixTransform* transform =
226
geometry->getParent(0)->getParent(0)->asTransform()->asMatrixTransform();
229
transform->setMatrix(transform->getMatrix() * osg::Matrix::translate(osg::Vec3(increment, 0, 0)));
234
_frameNumber = frameNumber;
236
geometry->dirtyBound();
241
// Drawcallback to draw averaged attribute
242
struct ValueTextDrawCallback : public virtual osg::Drawable::DrawCallback
244
ValueTextDrawCallback(osg::Stats* stats, const std::string& name):
246
_attributeName(name),
251
/** do customized draw code.*/
252
virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const
254
osgText::Text* text = (osgText::Text*)drawable;
256
int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber();
257
if (frameNumber == _frameNumber) {
258
text->drawImplementation(renderInfo);
263
if (_stats->getAttribute(_stats->getLatestFrameNumber(), _attributeName, value))
265
sprintf(_tmpText,"%4.2f",value);
266
text->setText(_tmpText);
272
_frameNumber = frameNumber;
273
text->drawImplementation(renderInfo);
276
osg::ref_ptr<osg::Stats> _stats;
277
std::string _attributeName;
278
mutable char _tmpText[128];
279
mutable int _frameNumber;
290
osg::ref_ptr<osg::Group> _group;
291
osg::ref_ptr<osg::Geode> _label;
292
osg::ref_ptr<osg::MatrixTransform> _graph;
293
osg::ref_ptr<osgText::Text> _textLabel;
294
osgAnimation::OutCubicMotion _fade;
296
StatAction() { _lastTime = 0; _fade = osgAnimation::OutCubicMotion(0,5); }
297
void init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float heigh, const osg::Vec4& color);
298
void setPosition(const osg::Vec3& pos);
302
_lastTime = osg::Timer::instance()->time_s();
303
float a = 1.0 - _fade.getValueAt(0.0);
307
double t = osg::Timer::instance()->time_s();
308
float alpha = 1.0 - _fade.getValueAt(t-_lastTime);
309
if (t - _lastTime > _fade.getDuration())
315
void setAlpha(float v);
319
struct StatsTimeline : public osg::NodeCallback
321
static float _statsHeight;
322
static float _statsWidth;
324
osg::ref_ptr<osg::Geometry> _background;
325
osg::ref_ptr<osgAnimation::Timeline> _timeline;
326
osg::ref_ptr<osg::MatrixTransform> _group;
327
std::map<std::string, StatAction > _actions;
334
osg::MatrixTransform* createStatsForTimeline(osgAnimation::Timeline* timeline)
336
_timeline = timeline;
338
std::string font("fonts/arial.ttf");
340
float leftPos = 10.0f;
341
float startBlocks = 150.0f;
342
float characterSize = 20.0f;
345
osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3);
346
float backgroundMargin = 5;
347
//float backgroundSpacing = 3;
349
osg::Vec4 color(1.0, 1.0, 1.0, 1.0);
351
_group = new osg::MatrixTransform;
352
_group->setDataVariance(osg::Object::DYNAMIC);
355
osg::Vec3 pos(leftPos, _statsHeight-24.0f,0.0f);
356
//float topOfViewerStats = pos.y() + characterSize;
357
osg::ref_ptr<osg::Stats> stats = _timeline->getStats();
358
pos.y() -= characterSize + backgroundMargin;
361
osg::Geode* geode = new osg::Geode();
362
_group->addChild(geode);
363
osg::ref_ptr<osgText::Text> timeLabel = new osgText::Text;
364
geode->addDrawable( timeLabel.get() );
366
timeLabel->setColor(color);
367
timeLabel->setFont(font);
368
timeLabel->setCharacterSize(characterSize);
369
timeLabel->setPosition(pos);
370
timeLabel->setText("Time: ");
372
osg::ref_ptr<osgText::Text> timeLabelValue = new osgText::Text;
373
geode->addDrawable( timeLabelValue.get() );
375
timeLabelValue->setColor(color);
376
timeLabelValue->setFont(font);
377
timeLabelValue->setCharacterSize(characterSize);
378
timeLabelValue->setPosition(pos + osg::Vec3(startBlocks, 0,0));
379
timeLabelValue->setText("0.0");
381
timeLabelValue->setDrawCallback(new ValueTextDrawCallback(stats.get(),"Timeline"));
385
osg::Vec3 pos(leftPos, _statsHeight - 24.0f ,0.0f);
386
//float topOfViewerStats = pos.y();
387
osg::Geode* geode = new osg::Geode;
388
_background = createBackgroundRectangle(
389
pos + osg::Vec3(-backgroundMargin, backgroundMargin, 0),
390
_statsWidth - 2 * backgroundMargin,
391
(3 + 4.5 * 1) * characterSize + 2 * backgroundMargin,
393
geode->addDrawable(_background.get());
394
_group->addChild(geode);
400
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
402
if (nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) {
410
osgAnimation::StatsActionVisitor* visitor = _timeline->getStatsVisitor();
414
std::string font("fonts/arial.ttf");
415
float leftPos = 10.0f;
416
float characterSize = 20.0f;
418
float backgroundMargin = 5;
419
//float backgroundSpacing = 3;
420
float graphSpacing = 5;
422
float width = _statsWidth - 4 * backgroundMargin;
423
float height = characterSize;
424
osg::Vec3 pos(leftPos, _statsHeight-24.0f,0.0f);
425
pos.y() -= characterSize *2 + backgroundMargin;
427
for (std::map<std::string, StatAction >::iterator it = _actions.begin(); it != _actions.end(); ++it) {
428
(*it).second._group->setNodeMask(~osg::Node::NodeMask(1));
431
const std::vector<std::string>& channels = visitor->getChannels();
432
std::map<std::string,int> size;
433
for (int i = 0; i < (int)channels.size(); i++) {
434
std::string name = channels[i];
435
if (_actions.find(name) == _actions.end()) {
436
osg::Vec4 color(getRandomValueinRange(255)/255.0, getRandomValueinRange(255)/255.0, getRandomValueinRange(255)/255.0, 1.0);
437
_actions[name].init(visitor->getStats(), name, pos, width, height, color);
438
_group->addChild(_actions[name]._group.get());
439
//_actions[name].touch();
441
_actions[name].setPosition(pos);
442
//_actions[name].touch();
444
_actions[name]._group->setNodeMask(~osg::Node::NodeMask(0x0));
446
pos.y() -= characterSize + graphSpacing;
449
pos.y() -= backgroundMargin;
450
osg::Vec3Array* array = dynamic_cast<osg::Vec3Array*>(_background->getVertexArray());
451
float y = (*array)[0][1];
452
y = y - (pos.y() + backgroundMargin); //(2 * backgroundMargin + (size.size() * (characterSize + graphSpacing)));
453
(*array)[1][1] = pos.y();
454
(*array)[2][1] = pos.y();
456
_background->dirtyBound();
459
float StatsTimeline::_statsHeight;
460
float StatsTimeline::_statsWidth;
464
struct FindTimelineStats : public osg::NodeVisitor
466
std::vector<osg::ref_ptr<osgAnimation::Timeline> > _timelines;
468
FindTimelineStats() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
470
void apply(osg::Node& node) {
471
osg::NodeCallback* cb = node.getUpdateCallback();
473
osgAnimation::TimelineAnimationManager* tam = dynamic_cast<osgAnimation::TimelineAnimationManager*>(cb);
475
_timelines.push_back(tam->getTimeline());
476
cb = cb->getNestedCallback();
483
StatsHandler::StatsHandler():
484
_keyEventTogglesOnScreenStats('a'),
485
_keyEventPrintsOutStats('A'),
486
_statsType(NO_STATS),
488
_statsWidth(1280.0f),
489
_statsHeight(1024.0f)
491
_camera = new osg::Camera;
492
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
493
_camera->setProjectionResizePolicy(osg::Camera::FIXED);
496
bool StatsHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
498
osgViewer::View* myview = dynamic_cast<osgViewer::View*>(&aa);
499
if (!myview) return false;
501
osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(myview->getViewerBase());
503
if (!viewer->getSceneData())
505
if (ea.getHandled()) return false;
507
switch(ea.getEventType())
509
case(osgGA::GUIEventAdapter::KEYDOWN):
511
if (ea.getKey()==_keyEventTogglesOnScreenStats)
513
if (viewer->getViewerStats())
516
if (!_switch.get()) {
517
FindTimelineStats finder;
518
viewer->getSceneData()->accept(finder);
519
if (finder._timelines.empty())
525
setUpHUDCamera(viewer);
526
setUpScene(dynamic_cast<osgViewer::Viewer*>(viewer));
531
if (_statsType==LAST) _statsType = NO_STATS;
538
_camera->setNodeMask(0x0);
539
_switch->setAllChildrenOff();
544
_camera->setNodeMask(0xffffffff);
545
_switch->setAllChildrenOn();
556
if (ea.getKey()==_keyEventPrintsOutStats)
558
FindTimelineStats finder;
559
viewer->getSceneData()->accept(finder);
560
if (!finder._timelines.empty()) {
561
osg::notify(osg::NOTICE)<<std::endl<<"Stats report:"<<std::endl;
562
typedef std::vector<osg::Stats*> StatsList;
565
for (int i = 0; i < (int)finder._timelines.size(); i++)
566
statsList.push_back(finder._timelines[i]->getStats());
568
for(int i = statsList[0]->getEarliestFrameNumber(); i<= statsList[0]->getLatestFrameNumber()-1; ++i)
570
for(StatsList::iterator itr = statsList.begin();
571
itr != statsList.end();
574
if (itr==statsList.begin()) (*itr)->report(osg::notify(osg::NOTICE), i);
575
else (*itr)->report(osg::notify(osg::NOTICE), i, " ");
577
osg::notify(osg::NOTICE)<<std::endl;
591
void StatsHandler::reset()
593
_initialized = false;
594
_camera->setGraphicsContext(0);
595
_camera->removeChildren( 0, _camera->getNumChildren() );
598
void StatsHandler::setUpHUDCamera(osgViewer::ViewerBase* viewer)
600
osgViewer::GraphicsWindow* window = dynamic_cast<osgViewer::GraphicsWindow*>(_camera->getGraphicsContext());
604
osgViewer::Viewer::Windows windows;
605
viewer->getWindows(windows);
607
if (windows.empty()) return;
609
window = windows.front();
612
_camera->setGraphicsContext(window);
614
_camera->setViewport(0, 0, window->getTraits()->width, window->getTraits()->height);
616
_camera->setRenderOrder(osg::Camera::POST_RENDER, 10);
618
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,0.0,_statsHeight));
619
_camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
620
_camera->setViewMatrix(osg::Matrix::identity());
622
// only clear the depth buffer
623
_camera->setClearMask(0); //GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //-1);
624
_camera->setAllowEventFocus(false);
625
_camera->setCullMask(0x1);
626
osgViewer::Viewer* v = dynamic_cast<osgViewer::Viewer*>(viewer);
627
v->getSceneData()->asGroup()->addChild(_camera.get());
631
void StatsHandler::setUpScene(osgViewer::Viewer* viewer)
633
if (!viewer->getSceneData())
636
FindTimelineStats finder;
637
viewer->getSceneData()->accept(finder);
638
if (finder._timelines.empty())
641
_switch = new osg::Switch;
642
osg::StateSet* stateset = _switch->getOrCreateStateSet();
643
stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
644
stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
645
stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
646
stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED);
648
_group = new osg::Group;
649
_camera->addChild(_switch.get());
650
_switch->addChild(_group.get());
652
for (int i = 0; i < (int)finder._timelines.size(); i++) {
653
StatsTimeline* s = new StatsTimeline;
654
osg::MatrixTransform* m = s->createStatsForTimeline(finder._timelines[i].get());
655
m->setUpdateCallback(s);
656
m->setMatrix(osg::Matrix::translate(0, -i * 100, 0));
664
void StatAction::init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float height, const osg::Vec4& color)
666
std::string font("fonts/arial.ttf");
667
float characterSize = 20.0f;
668
float startBlocks = 150.0f;
671
_group = new osg::Group;
673
_label = new osg::Geode;
674
_textLabel = new osgText::Text;
675
_label->addDrawable(_textLabel.get());
676
_textLabel->setDataVariance(osg::Object::DYNAMIC);
677
_textLabel->setColor(color);
678
_textLabel->setFont(font);
679
_textLabel->setCharacterSize(characterSize);
680
_textLabel->setPosition(pos - osg::Vec3(0, height, 0));
681
_textLabel->setText(name);
683
StatsGraph* graph = new StatsGraph(pos + osg::Vec3(startBlocks, 0,0) , width-startBlocks, height);
684
graph->setCullingActive(false);
685
graph->addStatGraph(stats, stats, color, 1.0, name);
688
_group->addChild(_label.get());
689
_group->addChild(_graph.get());
691
void StatAction::setAlpha(float v)
693
std::cout << this << " color alpha " << v << std::endl;
694
StatsGraph* gfx = dynamic_cast<StatsGraph*>(_graph.get());
695
osg::Vec4 color = _textLabel->getColor();
697
_textLabel->setColor(color);
698
for (int i = 0; i < (int) gfx->_statsGraphGeode->getNumDrawables(); i++) {
699
StatsGraph::Graph* g = dynamic_cast<StatsGraph::Graph*>(gfx->_statsGraphGeode->getDrawable(0));
704
void StatAction::setPosition(const osg::Vec3& pos)
706
float characterSize = 20.0f;
707
StatsGraph* gfx = dynamic_cast<StatsGraph*>(_graph.get());
708
gfx->changeYposition(pos[1]);
709
_textLabel->setPosition(pos - osg::Vec3(0, characterSize,0));
716
void StatsHandler::getUsage(osg::ApplicationUsage& usage) const
718
usage.addKeyboardMouseBinding("s","On screen stats.");
719
usage.addKeyboardMouseBinding("S","Output stats to console.");