Files
OpenSceneGraph/src/osgAnimation/StatsHandler.cpp

722 lines
24 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <sstream>
#include <iomanip>
#include <stdio.h>
#include <cmath>
#include <osg/io_utils>
#include <osg/NodeVisitor>
#include <osg/MatrixTransform>
#include <osgViewer/Renderer>
#include <osgAnimation/StatsHandler>
#include <osgAnimation/EaseMotion>
#include <osgAnimation/StatsVisitor>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Renderer>
#include <osgAnimation/TimelineAnimationManager>
#include <osg/PolygonMode>
#include <osg/Geometry>
#include <iostream>
#include <cstdlib>
static unsigned int getRandomValueinRange(unsigned int v)
{
return static_cast<unsigned int>((rand() * 1.0 * v)/(RAND_MAX-1));
}
namespace osgAnimation
{
osg::Geometry* createBackgroundRectangle(const osg::Vec3& pos, const float width, const float height, osg::Vec4& color)
{
osg::StateSet *ss = new osg::StateSet;
osg::Geometry* geometry = new osg::Geometry;
geometry->setUseDisplayList(false);
geometry->setStateSet(ss);
osg::Vec3Array* vertices = new osg::Vec3Array;
geometry->setVertexArray(vertices);
vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0));
vertices->push_back(osg::Vec3(pos.x(), pos.y()-height,0));
vertices->push_back(osg::Vec3(pos.x()+width, pos.y()-height,0));
vertices->push_back(osg::Vec3(pos.x()+width, pos.y(),0));
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
geometry->setColorArray(colors);
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
osg::DrawElementsUInt *base = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS,0);
base->push_back(0);
base->push_back(1);
base->push_back(2);
base->push_back(3);
geometry->addPrimitiveSet(base);
return geometry;
}
struct StatsGraph : public osg::MatrixTransform
{
StatsGraph(osg::Vec3 pos, float width, float height)
: _pos(pos), _width(width), _height(height),
_statsGraphGeode(new osg::Geode)
{
_pos = pos - osg::Vec3(0, _height, 0.1);
setMatrix(osg::Matrix::translate(_pos));
setDataVariance(osg::Object::DYNAMIC);
addChild(_statsGraphGeode.get());
_statsGraphGeode->setCullingActive(false);
}
void changeYposition(float y)
{
osg::Vec3 _pos = getMatrix().getTrans();
_pos[1] = y - _height;
setMatrix(osg::Matrix::translate(_pos));
}
void addStatGraph(osg::Stats* viewerStats, osg::Stats* stats, const osg::Vec4& color, float max, const std::string& nameBegin, const std::string& nameEnd = "")
{
_statsGraphGeode->addDrawable(new Graph(_width, _height, viewerStats, stats, color, max, nameBegin, nameEnd));
}
osg::Vec3 _pos;
float _width;
float _height;
osg::ref_ptr<osg::Geode> _statsGraphGeode;
struct NeverCull : public osg::Drawable::CullCallback
{
NeverCull() {}
bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::RenderInfo* renderInfo) const { return false;}
};
struct Graph : public osg::Geometry
{
Graph(float width, float height, osg::Stats* viewerStats, osg::Stats* stats,
const osg::Vec4& color, float max, const std::string& nameBegin, const std::string& nameEnd = "")
{
setDataVariance(osg::Object::DYNAMIC);
setUseDisplayList(false);
setVertexArray(new osg::Vec3Array);
getVertexArray()->setDataVariance(osg::Object::DYNAMIC);
setColor(color);
setUpdateCallback(new GraphUpdateCallback(width, height, viewerStats, stats, max, nameBegin, nameEnd));
setCullCallback(new NeverCull);
}
void setColor(const osg::Vec4& color) {
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
setColorArray(colors);
setColorBinding(osg::Geometry::BIND_OVERALL);
}
};
struct GraphUpdateCallback : public osg::Drawable::UpdateCallback
{
const unsigned int _width;
const unsigned int _height;
mutable unsigned int _curX;
osg::Stats* _viewerStats;
osg::Stats* _stats;
const float _max;
const std::string _nameBegin;
const std::string _nameEnd;
mutable int _frameNumber;
GraphUpdateCallback(float width, float height, osg::Stats* viewerStats, osg::Stats* stats,
float max, const std::string& nameBegin, const std::string& nameEnd = "")
: _width((unsigned int)width), _height((unsigned int)height), _curX(0),
_viewerStats(viewerStats), _stats(stats), _max(max), _nameBegin(nameBegin), _nameEnd(nameEnd), _frameNumber(0)
{
}
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable)
{
if (nv->getVisitorType() != osg::NodeVisitor::UPDATE_VISITOR)
return;
osg::Geometry* geometry = const_cast<osg::Geometry*>(drawable->asGeometry());
if (!geometry) return;
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
if (!vertices) return;
int frameNumber = nv->getFrameStamp()->getFrameNumber();
if (frameNumber == _frameNumber)
return;
// Get stats
double value;
if (_nameEnd.empty())
{
if (!_stats->getAttribute(_stats->getLatestFrameNumber(), _nameBegin, value ))
{
value = 0.0;
}
}
else
{
double beginValue, endValue;
if (_stats->getAttribute( frameNumber, _nameBegin, beginValue) &&
_stats->getAttribute( frameNumber, _nameEnd, endValue) )
{
value = endValue - beginValue;
}
else
{
value = 0.0;
}
}
// Add new vertex for this frame.
value = osg::clampTo(value, 0.0, double(_max));
if (!vertices->size()) {
for (int i = 0; i < (int)_width; i++)
vertices->push_back(osg::Vec3(float(_curX++), 0, 0));
// Create primitive set if none exists.
if (geometry->getNumPrimitiveSets() == 0)
geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, 0));
osg::DrawArrays* drawArrays = dynamic_cast<osg::DrawArrays*>(geometry->getPrimitiveSet(0));
drawArrays->setFirst(0);
drawArrays->setCount(vertices->size());
}
vertices->push_back(osg::Vec3(float(_curX), float(_height) / _max * value, 0));
unsigned int excedent = vertices->size() - _width;
vertices->erase(vertices->begin(), vertices->begin() + excedent);
// Make the graph scroll when there is enough data.
// Note: We check the frame number so that even if we have
// many graphs, the transform is translated only once per
// frame.
static const float increment = -1.0;
if (_frameNumber != frameNumber)
{
// We know the exact layout of this part of the scene
// graph, so this is OK...
osg::MatrixTransform* transform =
geometry->getParent(0)->getParent(0)->asTransform()->asMatrixTransform();
if (transform)
{
transform->setMatrix(transform->getMatrix() * osg::Matrix::translate(osg::Vec3(increment, 0, 0)));
}
}
_curX++;
_frameNumber = frameNumber;
geometry->dirtyBound();
}
};
};
// Drawcallback to draw averaged attribute
struct ValueTextDrawCallback : public virtual osg::Drawable::DrawCallback
{
ValueTextDrawCallback(osg::Stats* stats, const std::string& name):
_stats(stats),
_attributeName(name),
_frameNumber(0)
{
}
/** do customized draw code.*/
virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const
{
osgText::Text* text = (osgText::Text*)drawable;
int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber();
if (frameNumber == _frameNumber) {
text->drawImplementation(renderInfo);
return;
}
double value;
if (_stats->getAttribute(_stats->getLatestFrameNumber(), _attributeName, value))
{
sprintf(_tmpText,"%4.2f",value);
text->setText(_tmpText);
}
else
{
text->setText("");
}
_frameNumber = frameNumber;
text->drawImplementation(renderInfo);
}
osg::ref_ptr<osg::Stats> _stats;
std::string _attributeName;
mutable char _tmpText[128];
mutable int _frameNumber;
};
struct StatAction
{
double _lastTime;
std::string _name;
osg::ref_ptr<osg::Group> _group;
osg::ref_ptr<osg::Geode> _label;
osg::ref_ptr<osg::MatrixTransform> _graph;
osg::ref_ptr<osgText::Text> _textLabel;
osgAnimation::OutCubicMotion _fade;
StatAction() { _lastTime = 0; _fade = osgAnimation::OutCubicMotion(0,5); }
void init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float heigh, const osg::Vec4& color);
void setPosition(const osg::Vec3& pos);
#if 0
void touch()
{
_lastTime = osg::Timer::instance()->time_s();
float a = 1.0 - _fade.getValueAt(0.0);
setAlpha(a);
}
bool update() {
double t = osg::Timer::instance()->time_s();
float alpha = 1.0 - _fade.getValueAt(t-_lastTime);
if (t - _lastTime > _fade.getDuration())
return true;
setAlpha(alpha);
return false;
}
#endif
void setAlpha(float v);
};
struct StatsTimeline : public osg::NodeCallback
{
static float _statsHeight;
static float _statsWidth;
osg::ref_ptr<osg::Geometry> _background;
osg::ref_ptr<osgAnimation::Timeline> _timeline;
osg::ref_ptr<osg::MatrixTransform> _group;
std::map<std::string, StatAction > _actions;
StatsTimeline()
{
_statsHeight = 1024;
_statsWidth = 1280;
}
osg::MatrixTransform* createStatsForTimeline(osgAnimation::Timeline* timeline)
{
_timeline = timeline;
std::string font("fonts/arial.ttf");
float leftPos = 10.0f;
float startBlocks = 150.0f;
float characterSize = 20.0f;
osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3);
float backgroundMargin = 5;
//float backgroundSpacing = 3;
osg::Vec4 color(1.0, 1.0, 1.0, 1.0);
_group = new osg::MatrixTransform;
_group->setDataVariance(osg::Object::DYNAMIC);
{
osg::Vec3 pos(leftPos, _statsHeight-24.0f,0.0f);
//float topOfViewerStats = pos.y() + characterSize;
osg::ref_ptr<osg::Stats> stats = _timeline->getStats();
pos.y() -= characterSize + backgroundMargin;
{
osg::Geode* geode = new osg::Geode();
_group->addChild(geode);
osg::ref_ptr<osgText::Text> timeLabel = new osgText::Text;
geode->addDrawable( timeLabel.get() );
timeLabel->setColor(color);
timeLabel->setFont(font);
timeLabel->setCharacterSize(characterSize);
timeLabel->setPosition(pos);
timeLabel->setText("Time: ");
osg::ref_ptr<osgText::Text> timeLabelValue = new osgText::Text;
geode->addDrawable( timeLabelValue.get() );
timeLabelValue->setColor(color);
timeLabelValue->setFont(font);
timeLabelValue->setCharacterSize(characterSize);
timeLabelValue->setPosition(pos + osg::Vec3(startBlocks, 0,0));
timeLabelValue->setText("0.0");
timeLabelValue->setDrawCallback(new ValueTextDrawCallback(stats.get(),"Timeline"));
}
}
{
osg::Vec3 pos(leftPos, _statsHeight - 24.0f ,0.0f);
//float topOfViewerStats = pos.y();
osg::Geode* geode = new osg::Geode;
_background = createBackgroundRectangle(
pos + osg::Vec3(-backgroundMargin, backgroundMargin, 0),
_statsWidth - 2 * backgroundMargin,
(3 + 4.5 * 1) * characterSize + 2 * backgroundMargin,
backgroundColor);
geode->addDrawable(_background.get());
_group->addChild(geode);
}
return _group.get();
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) {
updateGraph();
}
traverse(node,nv);
}
void updateGraph()
{
osgAnimation::StatsActionVisitor* visitor = _timeline->getStatsVisitor();
if (!visitor)
return;
std::string font("fonts/arial.ttf");
float leftPos = 10.0f;
float characterSize = 20.0f;
float backgroundMargin = 5;
//float backgroundSpacing = 3;
float graphSpacing = 5;
float width = _statsWidth - 4 * backgroundMargin;
float height = characterSize;
osg::Vec3 pos(leftPos, _statsHeight-24.0f,0.0f);
pos.y() -= characterSize *2 + backgroundMargin;
for (std::map<std::string, StatAction >::iterator it = _actions.begin(); it != _actions.end(); it++) {
//if ((*it).second.update())
(*it).second._group->setNodeMask(~1);
}
const std::vector<std::string>& channels = visitor->getChannels();
std::map<std::string,int> size;
for (int i = 0; i < (int)channels.size(); i++) {
std::string name = channels[i];
if (_actions.find(name) == _actions.end()) {
osg::Vec4 color(getRandomValueinRange(255)/255.0, getRandomValueinRange(255)/255.0, getRandomValueinRange(255)/255.0, 1.0);
_actions[name].init(visitor->getStats(), name, pos, width, height, color);
_group->addChild(_actions[name]._group.get());
//_actions[name].touch();
} else {
_actions[name].setPosition(pos);
//_actions[name].touch();
}
_actions[name]._group->setNodeMask(-1);
size[name] = 0;
pos.y() -= characterSize + graphSpacing;
}
pos.y() -= backgroundMargin;
osg::Vec3Array* array = dynamic_cast<osg::Vec3Array*>(_background->getVertexArray());
float y = (*array)[0][1];
y = y - (pos.y() + backgroundMargin); //(2 * backgroundMargin + (size.size() * (characterSize + graphSpacing)));
(*array)[1][1] = pos.y();
(*array)[2][1] = pos.y();
array->dirty();
_background->dirtyBound();
}
};
float StatsTimeline::_statsHeight;
float StatsTimeline::_statsWidth;
struct FindTimelineStats : public osg::NodeVisitor
{
std::vector<osg::ref_ptr<osgAnimation::Timeline> > _timelines;
FindTimelineStats() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
void apply(osg::Node& node) {
osg::NodeCallback* cb = node.getUpdateCallback();
while (cb) {
osgAnimation::TimelineAnimationManager* tam = dynamic_cast<osgAnimation::TimelineAnimationManager*>(cb);
if (tam)
_timelines.push_back(tam->getTimeline());
cb = cb->getNestedCallback();
}
traverse(node);
}
};
StatsHandler::StatsHandler():
_keyEventTogglesOnScreenStats('a'),
_keyEventPrintsOutStats('A'),
_statsType(NO_STATS),
_initialized(false),
_statsWidth(1280.0f),
_statsHeight(1024.0f)
{
_camera = new osg::Camera;
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
_camera->setProjectionResizePolicy(osg::Camera::FIXED);
}
bool StatsHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
osgViewer::View* myview = dynamic_cast<osgViewer::View*>(&aa);
if (!myview) return false;
osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(myview->getViewerBase());
if (!viewer->getSceneData())
return false;
if (ea.getHandled()) return false;
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYDOWN):
{
if (ea.getKey()==_keyEventTogglesOnScreenStats)
{
if (viewer->getViewerStats())
{
if (!_switch.get()) {
FindTimelineStats finder;
viewer->getSceneData()->accept(finder);
if (finder._timelines.empty())
return false;
}
if (!_initialized)
{
setUpHUDCamera(viewer);
setUpScene(dynamic_cast<osgViewer::Viewer*>(viewer));
}
++_statsType;
if (_statsType==LAST) _statsType = NO_STATS;
switch(_statsType)
{
case(NO_STATS):
{
_camera->setNodeMask(0x0);
_switch->setAllChildrenOff();
break;
}
case(FRAME_RATE):
{
_camera->setNodeMask(0xffffffff);
_switch->setAllChildrenOn();
break;
}
default:
break;
}
}
return true;
}
if (ea.getKey()==_keyEventPrintsOutStats)
{
FindTimelineStats finder;
viewer->getSceneData()->accept(finder);
if (!finder._timelines.empty()) {
osg::notify(osg::NOTICE)<<std::endl<<"Stats report:"<<std::endl;
typedef std::vector<osg::Stats*> StatsList;
StatsList statsList;
for (int i = 0; i < (int)finder._timelines.size(); i++)
statsList.push_back(finder._timelines[0]->getStats());
for(int i = statsList[0]->getEarliestFrameNumber(); i<= statsList[0]->getLatestFrameNumber()-1; ++i)
{
for(StatsList::iterator itr = statsList.begin();
itr != statsList.end();
++itr)
{
if (itr==statsList.begin()) (*itr)->report(osg::notify(osg::NOTICE), i);
else (*itr)->report(osg::notify(osg::NOTICE), i, " ");
}
osg::notify(osg::NOTICE)<<std::endl;
}
}
return true;
}
}
default: break;
}
return false;
}
void StatsHandler::reset()
{
_initialized = false;
_camera->setGraphicsContext(0);
_camera->removeChildren( 0, _camera->getNumChildren() );
}
void StatsHandler::setUpHUDCamera(osgViewer::ViewerBase* viewer)
{
osgViewer::GraphicsWindow* window = dynamic_cast<osgViewer::GraphicsWindow*>(_camera->getGraphicsContext());
if (!window)
{
osgViewer::Viewer::Windows windows;
viewer->getWindows(windows);
if (windows.empty()) return;
window = windows.front();
}
_camera->setGraphicsContext(window);
_camera->setViewport(0, 0, window->getTraits()->width, window->getTraits()->height);
_camera->setRenderOrder(osg::Camera::POST_RENDER, 10);
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,0.0,_statsHeight));
_camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
_camera->setViewMatrix(osg::Matrix::identity());
// only clear the depth buffer
_camera->setClearMask(0); //GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //-1);
_camera->setAllowEventFocus(false);
_camera->setCullMask(0x1);
osgViewer::Viewer* v = dynamic_cast<osgViewer::Viewer*>(viewer);
v->getSceneData()->asGroup()->addChild(_camera.get());
_initialized = true;
}
void StatsHandler::setUpScene(osgViewer::Viewer* viewer)
{
if (!viewer->getSceneData())
return;
FindTimelineStats finder;
viewer->getSceneData()->accept(finder);
if (finder._timelines.empty())
return;
_switch = new osg::Switch;
osg::StateSet* stateset = _switch->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED);
_group = new osg::Group;
_camera->addChild(_switch.get());
_switch->addChild(_group.get());
for (int i = 0; i < (int)finder._timelines.size(); i++) {
StatsTimeline* s = new StatsTimeline;
osg::MatrixTransform* m = s->createStatsForTimeline(finder._timelines[i].get());
m->setUpdateCallback(s);
_group->addChild(m);
}
}
void StatAction::init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float height, const osg::Vec4& color)
{
std::string font("fonts/arial.ttf");
float characterSize = 20.0f;
float startBlocks = 150.0f;
_name = name;
_group = new osg::Group;
_label = new osg::Geode;
_textLabel = new osgText::Text;
_label->addDrawable(_textLabel.get());
_textLabel->setDataVariance(osg::Object::DYNAMIC);
_textLabel->setColor(color);
_textLabel->setFont(font);
_textLabel->setCharacterSize(characterSize);
_textLabel->setPosition(pos - osg::Vec3(0, height, 0));
_textLabel->setText(name);
StatsGraph* graph = new StatsGraph(pos + osg::Vec3(startBlocks, 0,0) , width-startBlocks, height);
graph->setCullingActive(false);
graph->addStatGraph(stats, stats, color, 1.0, name);
_graph = graph;
_group->addChild(_label.get());
_group->addChild(_graph.get());
}
void StatAction::setAlpha(float v)
{
std::cout << this << " color alpha " << v << std::endl;
StatsGraph* gfx = dynamic_cast<StatsGraph*>(_graph.get());
osg::Vec4 color = _textLabel->getColor();
color[3] = v;
_textLabel->setColor(color);
for (int i = 0; i < (int) gfx->_statsGraphGeode->getNumDrawables(); i++) {
StatsGraph::Graph* g = dynamic_cast<StatsGraph::Graph*>(gfx->_statsGraphGeode->getDrawable(0));
g->setColor(color);
}
}
void StatAction::setPosition(const osg::Vec3& pos)
{
float characterSize = 20.0f;
StatsGraph* gfx = dynamic_cast<StatsGraph*>(_graph.get());
gfx->changeYposition(pos[1]);
_textLabel->setPosition(pos - osg::Vec3(0, characterSize,0));
}
void StatsHandler::getUsage(osg::ApplicationUsage& usage) const
{
usage.addKeyboardMouseBinding("s","On screen stats.");
usage.addKeyboardMouseBinding("S","Output stats to console.");
}
}