#include
#include
#include
#include
#define GL_GLEXT_LOEGACY
#include
#undef GL_GLEXT_LEGACY
#include "Shader.hpp"
#include "Globals.hpp"
#include "config.h"
#include "ShrikeCanvas.hpp"
#include
using namespace SH;
using namespace ShUtil;
class EdgeGooch : public Shader {
public:
EdgeGooch();
~EdgeGooch();
bool init();
void initLists();
void bind();
void render();
void facemodeVert(const ShObjEdge *e0, const ShObjEdge* e1,
const ShObjEdge *e2, float move);
ShProgram vertex() { return vsh & edgevsh;}
ShProgram fragment() { return fsh & edgefsh; }
ShProgram vsh, edgevsh;
ShProgram fsh, edgefsh;
private:
ShObjMesh m_obj;
const ShObjMesh *m_canvasobj;
ShPoint3f eyePosm;
ShAttrib1f aspect;
GLuint displayList;
GLuint edgeDisplayList;
static EdgeGooch* instance;
};
EdgeGooch::EdgeGooch()
: Shader("Sillhouetted Gooch") {
m_canvasobj = 0;
}
EdgeGooch::~EdgeGooch() { }
void EdgeGooch::bind() { }
void EdgeGooch::render() {
const ShrikeCanvas *canvas = ShrikeCanvas::instance();
if( m_canvasobj != canvas->getModel()) {
m_canvasobj = canvas->getModel();
m_obj = *m_canvasobj;
m_obj.consolidateVertices();
m_obj.mergeEdges();
m_obj.normalizeNormals();
initLists();
}
aspect = canvas->m_width / (float)canvas->m_height;
// render fill
shBindShader(vsh);
shBindShader(fsh);
glCallList(displayList);
eyePosm = Globals::mv_inverse | ShPoint3f(0, 0, 0);
shBindShader(edgevsh);
shBindShader(edgefsh);
glCallList(edgeDisplayList);
}
bool EdgeGooch::init()
{
vsh = ShKernelLib::shVsh(Globals::mv, Globals::mvp);
vsh = vsh << shExtract("lightPos") << Globals::lightPos;
vsh = shSwizzle("normal", "lightVec", "posh") << vsh;
ShColor3f SH_NAMEDECL(diffuseColor, "Diffuse Color") = ShConstant3f(0.9f, 0.9f, 0.6f);
ShColor3f SH_NAMEDECL(cool, "Cool Color") = ShConstant3f(0.2f, 0.2f, 1.0f);
ShColor3f SH_NAMEDECL(warm, "Warm Color") = ShConstant3f(1.0f, 0.3f, 0.3f);
ShAttrib1f SH_NAMEDECL(width, "Edge Width") = ShConstant1f(0.02f);
width.range(0.001f, 0.5f);
ShAttrib1f SH_NAMEDECL(poffset, "Polygon Offset") = ShConstant1f(0.0f);
poffset.range(-1.0f, 1.0f);
fsh = SH_BEGIN_PROGRAM("gpu:fragment") { // gooch shader
ShInputNormal3f SH_DECL(normal);
ShInputVector3f SH_DECL(lightVec);
normal = normalize(normal);
lightVec= normalize(lightVec);
ShConstant1f half(0.5f);
ShAttrib1f blend = mad(dot(normal, lightVec), half, half);
ShOutputColor3f SH_DECL(result) = diffuseColor * lerp(blend, warm, cool);
} SH_END;
edgevsh = SH_BEGIN_PROGRAM("gpu:vertex") {
ShInOutNormal3f SH_NAMEDECL(n0, "normal"); // MCS normal (n0)
ShInOutTexCoord2f SH_NAMEDECL(tc0, "texcoord" );
ShInputTexCoord2f SH_DECL(tc1);
ShInputTexCoord2f SH_DECL(tc2);
ShInOutAttrib1f SH_DECL(move); //
ShInputPoint3f SH_DECL(p1);
ShInputNormal3f SH_DECL(n1);
ShInputPoint3f SH_DECL(p2);
ShInputNormal3f SH_DECL(n2);
ShInOutPosition4f SH_NAMEDECL(p0, "posm");
ShAttrib3f nvv; // nvv(i) holds dot product of ni and view vec to pi
nvv(0) = normalize(eyePosm - p0(0,1,2)) | n0;
nvv(1) = normalize(eyePosm - p1) | n1;
nvv(2) = normalize(eyePosm - p2) | n2;
// s(i) = 1 if there is a sign change between ni, ni+1
ShAttrib3f s = (nvv * nvv(1,2,0)) < 0.0f;
// find interpolated points between p0,p1 and p0,p2
ShAttrib2f k = -nvv(1,2) / (nvv(0,0) - nvv(1,2));
ShAttrib2f nk = ShConstant2f(1.0f, 1.0f) - k;
p1 = lerp(k(0), p0(0,1,2), p1);
n1 = lerp(k(0), n0, n1);
tc1 = lerp(k(0), tc0, tc1);
p2 = lerp(k(1), p0(0,1,2), p2);
n2 = lerp(k(1), n0, n2);
tc2 = lerp(k(1), tc0, tc2);
// pick which interpolated point to use based on conditions
ShAttrib1f snone = dot(s, s) < 0.5f; // not on sillhouete
ShAttrib1f nots2_and_s0 = (1.0f - s(2)) * s(0); //
p0(0,1,2) = snone * p0(0,1,2) + s(2) * p2 + nots2_and_s0 * p1;
n0 = snone * n0 + s(2) * n2 + nots2_and_s0 * n1;
tc0 = snone * tc0 + s(2) * tc2 + nots2_and_s0 * tc1;
move *= ( 1.0f - snone ); // only expand quad on sillhouette
} SH_END;
ShProgram translator = SH_BEGIN_PROGRAM("gpu:vertex") {
ShInputPosition4f SH_DECL(posm); // in MCS
ShInOutTexCoord2f SH_DECL(texcoord);
ShInOutNormal3f SH_DECL(normal); // in MCS, out VCS
ShInputAttrib1f SH_DECL(move); // whether to move
ShOutputPosition4f SH_DECL(posh); // out DCS
posh = Globals::mvp | posm;
ShPoint3f offposh = posm(0,1,2) + normal;
offposh = Globals::mvp | offposh;
ShPoint3f nph = posh(0,1,2) / posh(3);
ShVector2f offset = normalize(offposh - nph )(0,1) * width * move * posh(3);
offset(1) *= aspect;
posh(0,1) += offset;
posh(2) += poffset;
normal = normalize(Globals::mv | normal);
texcoord(0) += texcoord(1);
texcoord(1) = move;
} SH_END;
edgevsh = namedConnect( edgevsh, translator );
edgefsh = SH_BEGIN_PROGRAM("gpu:fragment") {
ShOutputColor4f SH_DECL(kd) = ShConstant4f(0.0f, 0.0f, 0.0f, 1.0f);
} SH_END;
return true;
}
void EdgeGooch::facemodeVert(const ShObjEdge *e0, const ShObjEdge* e1,
const ShObjEdge *e2, float move) {
// Note, because of current backend binding order, texcoords must go first
float vs[4];
vs[3] = 0;
e0->normal.getValues(vs); glNormal3fv(vs);
e0->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0, vs);
e1->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0 + 1, vs);
e2->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0 + 2, vs);
glMultiTexCoord1fARB(GL_TEXTURE0 + 3, move);
e1->start->pos.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 4, vs);
e1->normal.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 5, vs);
e2->start->pos.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 6, vs);
e2->normal.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 7, vs);
e0->start->pos.getValues(vs); glVertex3fv(vs);
}
void EdgeGooch::initLists() {
float vs[4];
displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE);
glBegin(GL_TRIANGLES);
for(ShObjMesh::FaceSet::iterator I = m_obj.faces.begin();
I != m_obj.faces.end(); ++I) {
ShObjMesh::Edge* e = (*I)->edge;
do {
e->normal.getValues(vs); glNormal3fv(vs);
e->texcoord.getValues(vs); glTexCoord2fv(vs);
e->start->pos.getValues(vs); glVertex3fv(vs);
e = e->next;
} while( e != (*I)->edge);
}
glEnd();
glEndList();
ShObjMesh fixedObj = m_obj;
fixedObj.generateVertexNormals(true); // force generate a single normal per vertex
fixedObj.normalizeNormals();
edgeDisplayList = glGenLists(1);
glNewList(edgeDisplayList, GL_COMPILE);
glBegin(GL_QUADS);
for(ShObjMesh::FaceSet::iterator I = fixedObj.faces.begin();
I != fixedObj.faces.end(); ++I) {
ShObjMesh::Edge *e0, *e1, *e2;
e0 = (*I)->edge;
e1 = e0->next;
e2 = e1->next;
// TODO cannot handle non-triangular faces yet
// (so make sure to triangulate the mesh)
SH_DEBUG_ASSERT( e2->next == e0 );
facemodeVert(e0, e1, e2, 0.0f);
facemodeVert(e1, e0, e2, 0.0f);
facemodeVert(e1, e0, e2, 1.0f);
facemodeVert(e0, e1, e2, 1.0f);
}
glEnd();
glEndList();
}
EdgeGooch* EdgeGooch::instance = new EdgeGooch();