Skip to content

Commit

Permalink
stroke is shadered, related to issue #20
Browse files Browse the repository at this point in the history
  • Loading branch information
vicrucann committed Aug 16, 2016
1 parent 0ca86e9 commit 4e55314
Show file tree
Hide file tree
Showing 7 changed files with 321 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ endif()

## copy shaders (only runs on cmake, not make command)
file (COPY ${CMAKE_SOURCE_DIR}/cherish/Shaders DESTINATION ${CMAKE_BINARY_DIR}/cherish)
file (COPY ${CMAKE_SOURCE_DIR}/cherish/Shaders DESTINATION ${CMAKE_BINARY_DIR}/tests)


## Installer settings (CPack) will be located here
124 changes: 115 additions & 9 deletions src/libSGEntities/Stroke.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ entity::Stroke::Stroke()
: entity::Entity2D()
, m_lines(new osg::DrawArrays(GL_LINE_STRIP_ADJACENCY))
, m_program(new osg::Program)
, m_camera(0)
, m_color(cher::STROKE_CLR_NORMAL)
, m_isShadered(false)
{
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(m_color);
Expand Down Expand Up @@ -57,6 +59,7 @@ entity::Stroke::Stroke(const entity::Stroke& copy, const osg::CopyOp& copyop)
, m_lines(copy.m_lines)
, m_program(copy.m_program)
, m_color(copy.m_color)
, m_isShadered(false)
{
qDebug("stroke copy ctor done");
}
Expand Down Expand Up @@ -89,6 +92,70 @@ const osg::Vec4f&entity::Stroke::getColor() const
return m_color;
}

const osg::Program *entity::Stroke::getProgram() const
{
return m_program.get();
}

bool entity::Stroke::copyFrom(const entity::Stroke *copy)
{
if (!copy){
qWarning("Copy stroke is NULL");
return false;
}
osg::Vec3Array* verts = static_cast<osg::Vec3Array*>(this->getVertexArray());
const osg::Vec3Array* vertsCopy = static_cast<const osg::Vec3Array*>(copy->getVertexArray());

if (this->getColor() != cher::STROKE_CLR_NORMAL ||
!this->getLines() ||
static_cast<int>(this->getLines()->getMode()) != GL_LINE_STRIP_ADJACENCY ||
!verts || !vertsCopy) {
qWarning("stroke::copyFrom() : stroke parameters check failed");
return false;
}

if (verts->size() != 0 || vertsCopy->size() == 0){
qWarning("stroke::copyFrom() : stroke size check failed");
return false;
}

if (!copy->isShadered()){
if (this->getColor() != cher::STROKE_CLR_NORMAL ||
!this->getLines() ||
static_cast<int>(this->getLines()->getMode()) != GL_LINE_STRIP_ADJACENCY ||
static_cast<int>(copy->getLines()->getMode()) != GL_LINE_STRIP_ADJACENCY ||
!verts || !vertsCopy)
{
qWarning("stroke::copyFrom() : stroke parameters check failed");
return false;
}


for (unsigned int i=0; i<vertsCopy->size(); ++i){
osg::Vec2f p = copy->getPoint(i);
this->appendPoint(p.x(), p.y());
}
}
else {
if (static_cast<int>(copy->getLines()->getMode()) != GL_LINES_ADJACENCY_EXT){
qWarning("stroke::copyFrom : copy stroke geometry's mode failed");
return false;
}
if (vertsCopy->size() % 4 != 0 ){
qWarning("stroke::copyFrom : copy stroke vertex number must be divadable to 4");
return false;
}

for (unsigned int i=1; i<vertsCopy->size(); i+4){
osg::Vec2f p = copy->getPoint(i);
this->appendPoint(p.x(), p.y());
}
Q_ASSERT(verts->size() == vertsCopy->size()/4 + 1);
}

return true;
}

#endif /* DOXYGEN_SHOULD_SKIP_THIS */

void entity::Stroke::appendPoint(const float u, const float v)
Expand All @@ -110,6 +177,33 @@ void entity::Stroke::appendPoint(const float u, const float v)
// read more: http://forum.openscenegraph.org/viewtopic.php?t=2190&postdays=0&postorder=asc&start=15
}

osg::Vec2f entity::Stroke::getPoint(unsigned int i) const
{
const osg::Vec3Array* verts = static_cast<const osg::Vec3Array*>(this->getVertexArray());
if (!verts){
qWarning("Stroke vertices are not initialized. Cannot obtain a point.");
return osg::Vec2f(0.f, 0.f);
}
unsigned int sz = verts->size();
if (i>=sz){
qWarning("Stroke's index is out of range. Cannot obtain a point");
return osg::Vec2f(0.f, 0.f);
}
osg::Vec3f p = (*verts)[i];
if (std::fabs(p.z()) > cher::EPSILON){
qWarning("Stroke::getPoint : unexpected value of z-coordinate");
qInfo() << p.z();
return osg::Vec2f(0.f, 0.f);
}

return osg::Vec2f(p.x(), p.y());
}

osg::Camera *entity::Stroke::getCamera() const
{
return m_camera.get();
}

// read more on why: http://stackoverflow.com/questions/36655888/opengl-thick-and-smooth-non-broken-lines-in-3d
bool entity::Stroke::redefineToShader(osg::Camera *camera)
{
Expand All @@ -136,8 +230,11 @@ bool entity::Stroke::redefineToShader(osg::Camera *camera)
for (size_t i=0; i<originPts->size()-1; ++i){
/* first two point */
if (i==0){
/* first point is 0th index */
Q_ASSERT(idx < shaderPts->size());
(*shaderPts)[idx++] = (*originPts)[i];
osg::Vec3f dir0 = (*originPts)[i] - (*originPts)[i+1];
dir0.normalize();
(*shaderPts)[idx++] = (*originPts)[i] + dir0;
Q_ASSERT(idx < shaderPts->size());
(*shaderPts)[idx++] = (*originPts)[i];
}
Expand All @@ -149,17 +246,13 @@ bool entity::Stroke::redefineToShader(osg::Camera *camera)
}

/* last two points */
if (i==originPts->size()-1){
Q_ASSERT(idx < shaderPts->size());
(*shaderPts)[idx++] = (*originPts)[i];
Q_ASSERT(idx < shaderPts->size());
(*shaderPts)[idx++] = (*originPts)[i];
}
else if (i+1 == originPts->size()-1){
if (i+1 == originPts->size()-1){
Q_ASSERT(idx < shaderPts->size());
(*shaderPts)[idx++] = (*originPts)[i+1];
Q_ASSERT(idx < shaderPts->size());
(*shaderPts)[idx++] = (*originPts)[i+1];
osg::Vec3f dirn = (*originPts)[i+1] - (*originPts)[i];
dirn.normalize();
(*shaderPts)[idx++] = (*originPts)[i+1] + dirn;
}
else{
Q_ASSERT(idx < shaderPts->size());
Expand All @@ -186,9 +279,16 @@ bool entity::Stroke::redefineToShader(osg::Camera *camera)
Q_ASSERT(stateset);
stateset->setAttributeAndModes(m_program.get(), osg::StateAttribute::ON);

m_camera = camera;

return true;
}

bool entity::Stroke::isShadered() const
{
return m_isShadered;
}

float entity::Stroke::getLength() const
{
osg::BoundingBox bb = this->getBoundingBox();
Expand Down Expand Up @@ -258,6 +358,11 @@ cher::ENTITY_TYPE entity::Stroke::getEntityType() const

bool entity::Stroke::initializeShaderProgram(osg::Camera *camera)
{
if (!camera){
qWarning("Camera is NULL");
return false;
}

m_program->setName("DefaultStrokeShader");

/* load and add shaders to the program */
Expand Down Expand Up @@ -316,6 +421,7 @@ bool entity::Stroke::initializeShaderProgram(osg::Camera *camera)
float miterLimit = 0.75;
state->addUniform(new osg::Uniform("MiterLimit", miterLimit));

m_isShadered = true;
return true;
}

Expand Down
25 changes: 21 additions & 4 deletions src/libSGEntities/Stroke.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Stroke : public entity::Entity2D {
/*! Constructor that creates an empty stroke. */
Stroke();

/*! Copy constructor */
/*! Copy constructor, only used for serialization; not to be used otherwise. */
Stroke(const Stroke& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);

META_Node(entity, Stroke)
Expand All @@ -43,16 +43,31 @@ class Stroke : public entity::Entity2D {

inline void setColor(const osg::Vec4f& color);
inline const osg::Vec4f& getColor() const;
const osg::Program* getProgram() const;
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

/*! A method to be used to copy the input stroke's data. It is assumed *this stroke is empty.
* \param copy is the source stroke to copy from. */
bool copyFrom(const entity::Stroke* copy);

/*! A method to add a point to the end of a stroke. It is normally used when constructing a stroke in-motion while sketching.
* \param u is local U coordinate, \param v is local V coordinate. */
void appendPoint(const float u, const float v);

/*! \param i is the point index. \return point coordinates at the specified index. */
osg::Vec2f getPoint(unsigned int i) const;

/*! \return observer pointer on camera which helped to create a shadered version.
* \sa redefineToShader(). */
osg::Camera* getCamera() const;

/*! A method to tune the look of the stroke with smoother connections and thicker linewidth.
* So that to avoid broken and thin look of the default OpenGL functionality when using GL_LINE_STRIP_ADJACENCY and such. */
bool redefineToShader(osg::Camera* camera);

/*! \return whether a shader is applied to strokes, or not. */
bool isShadered() const;

/*! \return length of the stroke, which is measured as a largest dimention of the bounding box around the stroke. */
float getLength() const;

Expand Down Expand Up @@ -84,9 +99,11 @@ class Stroke : public entity::Entity2D {
bool initializeShaderProgram(osg::Camera* camera);

private:
osg::ref_ptr<osg::DrawArrays> m_lines;
osg::ref_ptr<osg::Program> m_program;
osg::Vec4f m_color;
osg::ref_ptr<osg::DrawArrays> m_lines;
osg::ref_ptr<osg::Program> m_program;
osg::observer_ptr<osg::Camera> m_camera;
osg::Vec4f m_color;
bool m_isShadered;
};
}

Expand Down
22 changes: 12 additions & 10 deletions src/libSGEntities/UserScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1029,18 +1029,20 @@ void entity::UserScene::strokeFinish(QUndoStack* stack)
entity::Stroke* stroke = m_canvasCurrent->getStrokeCurrent();
if (this->strokeValid()){
if (stroke->isLengthy()){
osg::ref_ptr<entity::Stroke> stroke_clone = new entity::Stroke(*stroke, osg::CopyOp::DEEP_COPY_ALL);
osg::ref_ptr<entity::Stroke> stroke_clone = new entity::Stroke;
Q_ASSERT(stroke_clone.get());

osg::Camera* camera = NULL;
emit this->requestCamera(camera);
if (camera){
stroke_clone->redefineToShader(camera);
fur::AddStrokeCommand* cmd = new fur::AddStrokeCommand(this, stroke_clone);
stack->push(cmd);
if (stroke_clone->copyFrom(stroke)){

osg::Camera* camera = NULL;
emit this->requestCamera(camera);
if (camera){
stroke_clone->redefineToShader(camera);
fur::AddStrokeCommand* cmd = new fur::AddStrokeCommand(this, stroke_clone);
stack->push(cmd);
}
else
qWarning("strokeFinish(): could not obtain camera");
}
else
qWarning("strokeFinish(): could not obtain camera");
}
}
else{
Expand Down
7 changes: 7 additions & 0 deletions src/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ add_executable(${CANVAS_NAME} ${CANVAS_SRC} ${CHERISH_SRC} ${IMAGE_RSC} ${BASEGU
target_link_libraries(${CANVAS_NAME} ${TEST_LIBRARIES})
add_test(${CANVAS_NAME} ${CANVAS_SRC} ${CHERISH_SRC} ${IMAGE_RSC} ${BASEGUITEST_SRC})

# Stroke test
set(STROKE_SRC StrokeTest.h StrokeTest.cpp ${BASEGUITEST_SRC})
set(STROKE_NAME test_Stroke)
add_executable(${STROKE_NAME} ${STROKE_SRC} ${CHERISH_SRC} ${IMAGE_RSC} ${BASEGUITEST_SRC})
target_link_libraries(${STROKE_NAME} ${TEST_LIBRARIES})
add_test(${STROKE_NAME} ${STROKE_SRC} ${CHERISH_SRC} ${IMAGE_RSC} ${BASEGUITEST_SRC})

# UserScene tests
set(USERSCENE_SRC UserSceneTest.h UserSceneTest.cpp ${BASEGUITEST_SRC})
set(USERSCENE_NAME test_UserScene)
Expand Down
Loading

0 comments on commit 4e55314

Please sign in to comment.