Skip to content

Instantly share code, notes, and snippets.

@vicrucann
Created December 13, 2016 15:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vicrucann/95ed58a08a46791fbc379dd363a4c1e2 to your computer and use it in GitHub Desktop.
Save vicrucann/95ed58a08a46791fbc379dd363a4c1e2 to your computer and use it in GitHub Desktop.
OpeneSceneGraph: using ray casting to perform drawing on 3D virtual plane
#include "Canvas.h"
entity::Canvas::Canvas()
: osg::Group()
, m_transform(new osg::MatrixTransform)
, m_switch(new osg::Switch)
, m_frameGeode(new osg::Geode)
, m_frameGeometry(new osg::Geometry)
, m_dataGeode(new osg::Geode)
, m_center(osg::Vec3(0,0,0))
, m_normal(osg::Vec3(0,-1,0))
{
osg::Vec3Array* vertices = new osg::Vec3Array;
vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) );
vertices->push_back( osg::Vec3(-1.0f, 0.0f, 1.0f) );
vertices->push_back( osg::Vec3(-1.0f, 0.0f, -1.f) );
vertices->push_back( osg::Vec3(1.0f, 0.0f, -1.0f) );
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) );
this->addChild(m_transform.get());
m_transform->addChild(m_switch.get());
m_switch->addChild(m_frameGeode.get(), true);
m_frameGeode->addDrawable(m_frameGeometry.get());
m_switch->addChild(m_dataGeode.get());
m_frameGeometry->setVertexArray(vertices);
m_frameGeometry->setColorArray(colors);
m_frameGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
m_frameGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP,0,4));
m_frameGeometry->dirtyDisplayList();
m_frameGeometry->dirtyBound();
}
void entity::Canvas::addStroke(Stroke *stroke)
{
m_dataGeode->addChild(stroke);
}
const entity::Stroke *entity::Canvas::getStroke(unsigned int i) const
{
return dynamic_cast<Stroke*>(m_dataGeode->getChild(i));
}
void entity::Canvas::doStroke(float u, float v, int mouse)
{
if (mouse == 0){
m_stroke = new entity::Stroke;
m_stroke->setBegin(u,v);
m_dataGeode->addDrawable(m_stroke);
}
m_stroke->setEnd(u, v);
if (mouse == 2)
m_stroke = 0;
}
const osg::Vec3 &entity::Canvas::getCenter() const
{
return m_center;
}
osg::Plane entity::Canvas::getPlane() const
{
osg::Plane plane(m_normal, m_center);
return plane;
}
osg::MatrixTransform *entity::Canvas::getTransform() const
{
return m_transform.get();
}
entity::Canvas::~Canvas()
{
}
#ifndef CANVAS_H
#define CANVAS_H
#include <osg/ref_ptr>
#include <osg/Group>
#include <osg/Switch>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include "Stroke.h"
namespace entity
{
class Canvas : public osg::Group
{
public:
Canvas();
void addStroke(Stroke* stroke);
const Stroke* getStroke(unsigned int i) const;
void doStroke(float u, float v, int mouse);
const osg::Vec3& getCenter() const;
osg::Plane getPlane() const;
osg::MatrixTransform* getTransform() const;
protected:
~Canvas();
private:
osg::ref_ptr<osg::MatrixTransform> m_transform;
osg::ref_ptr<osg::Switch> m_switch;
osg::ref_ptr<osg::Geode> m_frameGeode;
osg::ref_ptr<osg::Geometry> m_frameGeometry;
osg::ref_ptr<osg::Geode> m_dataGeode;
osg::ref_ptr<entity::Stroke> m_stroke;
osg::Vec3 m_center;
osg::Vec3 m_normal;
};
}
#endif // CANVAS_H
cmake_minimum_required(VERSION 2.8.11)
project(osg-raycast)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(OpenSceneGraph REQUIRED COMPONENTS osgDB osgGA osgUtil osgViewer)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(SOURCES
osg-raycast.cpp
Canvas.h
Canvas.cpp
Stroke.h
Stroke.cpp
EventHandler.h
EventHandler.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME}
${OPENSCENEGRAPH_LIBRARIES}
)
#include "EventHandler.h"
#include <iostream>
#include "vector"
#include <osgViewer/View>
#include <osg/Plane>
EventHandler::EventHandler(entity::Canvas *canvas)
: osgGA::GUIEventHandler()
, m_canvas(canvas)
{
}
bool EventHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
if (!( (ea.getEventType() == osgGA::GUIEventAdapter::PUSH && ea.getButtonMask()== osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON)
|| (ea.getEventType() == osgGA::GUIEventAdapter::DRAG && ea.getButtonMask()== osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON)
|| (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE && ea.getButton()==osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON)
))
return false;
double u=0, v=0;
switch (ea.getEventType()){
case osgGA::GUIEventAdapter::PUSH:
if (this->getRaytraceCanvasIntersection(ea,aa,u,v))
this->doStroke(u, v, 0);
break;
case osgGA::GUIEventAdapter::RELEASE:
if (this->getRaytraceCanvasIntersection(ea,aa,u,v))
this->doStroke(u, v, 2);
break;
case osgGA::GUIEventAdapter::DRAG:
if (this->getRaytraceCanvasIntersection(ea,aa,u,v))
this->doStroke(u, v, 1);
break;
default:
break;
}
return true;
}
void EventHandler::doStroke(double u, double v, int mouse)
{
m_canvas->doStroke(u, v, mouse);
}
bool EventHandler::getRaytraceCanvasIntersection(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, double &u, double &v)
{
double x = ea.getX();
double y = ea.getY();
osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa);
if (!viewer){
std::cerr << "getRaytraceCanvasIntersection(): could not read viewer" << std::endl;
return false;
}
osg::Camera* camera = viewer->getCamera();
if (!camera){
std::cout << "getRaytraceCanvasIntersection(): could not read camera" << std::endl;
return false;
}
if (!camera->getViewport()){
std::cerr << "getRaytraceIntersection(): could not read viewport" << std::endl;
return false;
}
osg::Matrix VPW = camera->getViewMatrix()
* camera->getProjectionMatrix()
* camera->getViewport()->computeWindowMatrix();
osg::Matrix invVPW;
if (!invVPW.invert(VPW)){
std::cerr << "getRaytraceIntersection(): could not invert View-projection-world matrix for ray casting" << std::endl;
return false;
}
/* Algorithm:
* Calcualte near and far point in global 3D.
* Intersect that segment with plane of canvas - 3D intersection point.
* Extract local 3D coords so that to create a stroke.
*/
osg::Vec3f nearPoint = osg::Vec3f(x, y, 0.f) * invVPW;
osg::Vec3f farPoint = osg::Vec3f(x, y, 1.f) * invVPW;
const osg::Plane plane = m_canvas->getPlane();
const osg::Vec3f center = m_canvas->getCenter();
std::vector<osg::Vec3f> ray(2);
ray[0] = nearPoint;
ray[1] = farPoint;
if (plane.intersect(ray)){ // 1 or -1: no intersection
std::cerr << "getRaytraceIntersection(): no intersection with the ray." << std::endl;
return false;
}
osg::Vec3f dir = farPoint-nearPoint;
if (! plane.dotProductNormal(dir)){ // denominator
std::cerr << "getRaytraceIntersection(): projected line is parallel to the canvas plane" << std::endl;
return false;
}
if (! plane.dotProductNormal(center-nearPoint)){
std::cerr << "getRaytraceIntersection(): plane contains the line, so no single intersection can be defined" << std::endl;
return false;
}
double len = plane.dotProductNormal(center-nearPoint) / plane.dotProductNormal(dir);
osg::Vec3f P = dir * len + nearPoint; // global 3D intersection
osg::Matrix M = m_canvas->getTransform()->getMatrix();
osg::Matrix invM;
if (!invM.invert(M)){
std::cerr << "getRaytraceIntersection(): could not invert model matrix" << std::endl;
return false;
}
osg::Vec3f p = P * invM; // local intersection
u=p.x();
v=p.z();
return true;
}
#ifndef EVENTHANDLER_H
#define EVENTHANDLER_H
#include <osg/observer_ptr>
#include <osgGA/GUIEventHandler>
#include <osgGA/GUIActionAdapter>
#include <osgGA/GUIEventAdapter>
#include "Canvas.h"
class EventHandler : public osgGA::GUIEventHandler
{
public:
EventHandler(entity::Canvas* canvas);
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
virtual void doStroke(double u, double v, int mouse);
protected:
bool getRaytraceCanvasIntersection(const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa,
double& u, double& v);
private:
osg::observer_ptr<entity::Canvas> m_canvas;
};
#endif // EVENTHANDLER_H
/*
* =====================================================================================
*
* Filename: osg-raycast.cpp
*
* Description:
*
* Version: 1.0
* Created: 01/12/2016 07:50:06 PM
* Revision: none
* Compiler: gcc
*
* Author: Victoria Rudakova, vicrucann@gmail.com
* Organization: vicrucann.github.io
*
* =====================================================================================
*/
#include <iostream>
#include <windows.h>
#include <osg/ref_ptr>
#include <osgViewer/Viewer>
#include "Canvas.h"
#include "Stroke.h"
#include "EventHandler.h"
int main(int, char**){
::SetProcessDPIAware();
osg::ref_ptr<entity::Canvas> canvas = new entity::Canvas;
osg::ref_ptr<EventHandler> EH = new EventHandler(canvas.get());
osgViewer::Viewer viewer;
viewer.setSceneData(canvas.get());
viewer.addEventHandler(EH.get());
viewer.setUpViewInWindow(100,100,300,300);
osg::Camera* cam = viewer.getCamera();
cam->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 0.f));
cam->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
osg::StateSet* stateset = canvas->getOrCreateStateSet();
stateset->setMode(GL_LINE_SMOOTH, osg::StateAttribute::ON);
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
return viewer.run();
}
#include "Stroke.h"
entity::Stroke::Stroke()
: osg::Geometry()
{
osg::Vec3Array* vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(0,0,0));
vertices->push_back(osg::Vec3(0.5,0,0.5));
osg::Vec4 color = osg::Vec4(1,0,0,1);
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
colors->push_back(color);
this->setVertexArray(vertices);
this->setColorArray(colors);
this->setColorBinding(osg::Geometry::BIND_OVERALL);
this->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 2));
this->setDataVariance(osg::Object::DYNAMIC);
this->setUseDisplayList(false);
this->setUseVertexBufferObjects(true);
}
void entity::Stroke::setColor(const osg::Vec4 &c)
{
osg::Vec4Array* colors = static_cast<osg::Vec4Array*>(this->getColorArray());
colors->front() = c;
colors->dirty();
}
void entity::Stroke::setBegin(float a, float b)
{
osg::Vec3Array* verts = static_cast<osg::Vec3Array*>(this->getVertexArray());
(*verts)[0] = osg::Vec3(a,0,b);
verts->dirty();
}
void entity::Stroke::setEnd(float c, float d)
{
osg::Vec3Array* verts = static_cast<osg::Vec3Array*>(this->getVertexArray());
(*verts)[1] = osg::Vec3(c,0,d);
verts->dirty();
}
void entity::Stroke::setBeginEnd(float a, float b, float c, float d)
{
osg::Vec3Array* verts = static_cast<osg::Vec3Array*>(this->getVertexArray());
if (verts->size() == 0) {
verts->push_back(osg::Vec3(a,0,b));
verts->push_back(osg::Vec3(c,0,d));
}
else {
(*verts)[0] = osg::Vec3(a,0,b);
(*verts)[1] = osg::Vec3(c,0,d);
}
verts->dirty();
}
entity::Stroke::~Stroke()
{
}
#ifndef STROKE_H
#define STROKE_H
#include <osg/Geometry>
namespace entity{
class Stroke : public osg::Geometry
{
public:
Stroke();
void setColor(const osg::Vec4& c);
void setBegin(float a, float b);
void setEnd(float c, float d);
void setBeginEnd(float a, float b, float c, float d);
protected:
~Stroke();
}; // class
} // namespace
#endif // STROKE_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment