Commit c45a1074 authored by Jan Möbius's avatar Jan Möbius
Browse files

Fix for wireframe/hiddenline on poly meshes.

refs #2029


git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@18317 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 860ad0cb
......@@ -248,7 +248,7 @@ struct ACGDLLEXPORT RenderObject
/**
* adds a texture to an specific stage and enables texture support in shaderDesc
*/
void addTexture(const Texture& _t,const size_t _stage)
void addTexture(const Texture& _t,const size_t _stage, bool _addToShaderGen = true)
{
if (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS < numTextures())
{
......@@ -256,7 +256,8 @@ struct ACGDLLEXPORT RenderObject
return;
}
textures_[_stage] = _t;
shaderDesc.addTextureType(_t.type,_t.shadow,_stage);
if (_addToShaderGen)
shaderDesc.addTextureType(_t.type,_t.shadow,_stage);
}
///clear all textures. Also affected on shaderDesc
......
......@@ -88,7 +88,10 @@ MeshNodeT(Mesh& _mesh,
anyPickingBaseIndex_(0),
perFaceTextureIndexAvailable_(false),
perFaceTextureCoordsAvailable_(false),
textureMap_(0)
textureMap_(0),
polyEdgeBuf_(0),
polyEdgeBufSize_(0),
polyEdgeBufTex_(0)
{
/// \todo : Handle vbo not supported
......@@ -618,8 +621,9 @@ void ACG::SceneGraph::MeshNodeT<Mesh>::getRenderObjects( IRenderer* _renderer, G
ro.depthFunc = GL_LESS;
ro.setMaterial(_mat);
ro.shaderDesc.geometryTemplateFile = "";
ro.shaderDesc.fragmentTemplateFile = "";
ro.shaderDesc.vertexTemplateFile = ""; //QString(props->vertexShader().c_str());
ro.shaderDesc.geometryTemplateFile = ""; //QString(props->geometryShader().c_str());
ro.shaderDesc.fragmentTemplateFile = ""; //QString(props->fragmentShader().c_str());
// ------------------------
// 1. setup drawMesh based on property source
......@@ -711,14 +715,41 @@ void ACG::SceneGraph::MeshNodeT<Mesh>::getRenderObjects( IRenderer* _renderer, G
// use geometry shaders to simulate line width
QString geomTemplate = ShaderProgGenerator::getShaderDir();
geomTemplate += "Wireframe/geometry.tpl";
geomTemplate += "Wireframe/geometry_wire.tpl";
QString fragTemplate = ShaderProgGenerator::getShaderDir();
fragTemplate += "Wireframe/fragment.tpl";
fragTemplate += "Wireframe/fragment_wire.tpl";
#ifdef GL_ARB_texture_buffer_object
if (!drawMesh_->getMeshCompiler()->isTriangleMesh())
{
// use modified shader for npoly meshes
// the shader can identify non-face edges with the poly edge buffer
updatePolyEdgeBuf();
if (polyEdgeBufTex_)
{
geomTemplate = ShaderProgGenerator::getShaderDir();
geomTemplate += "Wireframe/geometry_npoly_wire.tpl";
fragTemplate = ShaderProgGenerator::getShaderDir();
fragTemplate += "Wireframe/fragment_npoly_wire.tpl";
RenderObject::Texture roTex;
roTex.id = polyEdgeBufTex_;
roTex.type = GL_TEXTURE_BUFFER;
// bind poly edge buffer to texture stage 2
ro.setUniform("polyEdgeBuffer", 2);
ro.addTexture(roTex, 2, false);
}
}
#endif
ro.shaderDesc.geometryTemplateFile = geomTemplate;
ro.shaderDesc.fragmentTemplateFile = fragTemplate;
ro.setUniform("screenSize", Vec2f((float)_state.viewport_width(), (float)_state.viewport_height()));
ro.setUniform("lineWidth", _state.line_width());
add_face_RenderObjects(_renderer, &ro);
......@@ -737,15 +768,44 @@ void ACG::SceneGraph::MeshNodeT<Mesh>::getRenderObjects( IRenderer* _renderer, G
// use shaders to simulate line width
QString geomTemplate = ShaderProgGenerator::getShaderDir();
geomTemplate += "Wireframe/geometry.tpl";
geomTemplate += "Wireframe/geometry_hidden.tpl";
QString fragTemplate = ShaderProgGenerator::getShaderDir();
fragTemplate += "Wireframe/fragment_hidden.tpl";
#ifdef GL_ARB_texture_buffer_object
if (!drawMesh_->getMeshCompiler()->isTriangleMesh())
{
// use modified shader for npoly meshes
// the shader can identify non-face edges with the poly edge buffer
updatePolyEdgeBuf();
if (polyEdgeBufTex_)
{
geomTemplate = ShaderProgGenerator::getShaderDir();
geomTemplate += "Wireframe/geometry_npoly_hidden.tpl";
fragTemplate = ShaderProgGenerator::getShaderDir();
fragTemplate += "Wireframe/fragment_npoly_hidden.tpl";
RenderObject::Texture roTex;
roTex.id = polyEdgeBufTex_;
roTex.type = GL_TEXTURE_BUFFER;
// bind poly edge buffer to texture stage 2
ro.setUniform("polyEdgeBuffer", 2);
ro.addTexture(roTex, 2, false);
}
}
#endif
ro.shaderDesc.geometryTemplateFile = geomTemplate;
ro.shaderDesc.fragmentTemplateFile = fragTemplate;
ro.setUniform("screenSize", Vec2f((float)_state.viewport_width(), (float)_state.viewport_height()));
ro.setUniform("lineWidth", _state.line_width());
ro.setUniform("bkColor", _state.clear_color());
add_face_RenderObjects(_renderer, &ro);
......@@ -819,11 +879,14 @@ void ACG::SceneGraph::MeshNodeT<Mesh>::getRenderObjects( IRenderer* _renderer, G
{
case DrawModes::PRIMITIVE_POINT:
{
// use specular color for points
if (_drawMode.isAtomic() )
ro.emissive = ro.specular;
else
ro.emissive = OpenMesh::color_cast<ACG::Vec3f>(_state.overlay_color());
if (ro.shaderDesc.shadeMode == SG_SHADE_UNLIT)
{
// use specular color for points
if (_drawMode.isAtomic() )
ro.emissive = ro.specular;
else
ro.emissive = OpenMesh::color_cast<ACG::Vec3f>(_state.overlay_color());
}
// use shaders to simulate line width
QString geomTemplate = ShaderProgGenerator::getShaderDir();
......@@ -1517,6 +1580,79 @@ MeshNodeT<Mesh>::getDrawMesh()
}
template<class Mesh>
void MeshNodeT<Mesh>::updatePolyEdgeBuf()
{
#ifdef GL_ARB_texture_buffer_object
MeshCompiler* mc = drawMesh_->getMeshCompiler();
if (mc && !mc->isTriangleMesh())
{
// create/update the poly-edge buffer
if (!polyEdgeBuf_)
glGenBuffers(1, &polyEdgeBuf_);
const int nTris = mc->getNumTriangles();
const int newBufSize = (nTris/2+1);
if (polyEdgeBufSize_ != newBufSize)
{
glBindBuffer(GL_TEXTURE_BUFFER, polyEdgeBuf_);
// The poly-edge buffer is a texture buffer that stores one byte for each triangle, which encodes triangle edge properties.
// An inner edge is an edge that was added during the triangulation of a n-poly,
// whereas outer edges are edges that already exist in the input mesh object.
// This information is used in the wireframe/hiddenline geometry shader to identify edges, which should not be rendered.
// Buffer storage:
// each triangle uses 3 bits to mark edges as visible or hidden
// outer edge -> bit = 1 (visible)
// inner edge -> bit = 0 (hidden)
// each byte can store edges for two triangles and the remaining 2 bits are left unused
polyEdgeBufSize_ = newBufSize;
unsigned char* polyEdgeBufData = new unsigned char[newBufSize];
// set zero
memset(polyEdgeBufData, 0, newBufSize);
// build buffer
for (int i = 0; i < nTris; ++i)
{
int byteidx = i>>1;
int bitidx = (i&1) * 3;
for (int k = 0; k < 3; ++k)
if (mc->isFaceEdge(i, k))
polyEdgeBufData[byteidx] += 1 << (k + bitidx);
}
glBufferData(GL_TEXTURE_BUFFER, polyEdgeBufSize_, polyEdgeBufData, GL_STATIC_DRAW);
delete [] polyEdgeBufData;
polyEdgeBufData = 0;
// create texture object for the texture buffer
if (!polyEdgeBufTex_)
{
glGenTextures(1, &polyEdgeBufTex_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, polyEdgeBufTex_);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8UI, polyEdgeBuf_);
glBindTexture(GL_TEXTURE_2D, 0);
}
ACG::glCheckErrors();
}
}
#endif
}
//=============================================================================
} // namespace SceneGraph
} // namespace ACG
......
......@@ -482,6 +482,20 @@ public:
unsigned int getMemoryUsage();
private:
// poly edge buffer used for wireframe/hiddenline rendering with barycentric interpolation in geometry shader
GLuint polyEdgeBuf_;
// size in bytes of the poly edge buffer
int polyEdgeBufSize_;
// texture object for polyEdgeBuf
GLuint polyEdgeBufTex_;
void updatePolyEdgeBuf();
};
......
// declare IO mapping prototype
void sg_MapIO(const int);
// emit a screen aligned quad along the edge v1-v2
void createEdgeQuad(const int v0, const int v1, in vec2 screenSize, in float lineWidth)
{
vec4 p0 = gl_in[v0].gl_Position;
vec4 p1 = gl_in[v1].gl_Position;
// convert to screen space
p0.xy = p0.xy / p0.w * screenSize;
p1.xy = p1.xy / p1.w * screenSize;
// compute dir and normal
vec2 lineDir = p1.xy - p0.xy;
vec2 lineNormal = normalize(vec2(-lineDir.y, lineDir.x));
// create screen-aligned quad
vec2 offset = lineNormal * lineWidth;
sg_MapIO(v0); // default IO mapping
gl_Position = vec4( (p0.xy + offset) * p0.w / screenSize, p0.z, p0.w);
EmitVertex();
sg_MapIO(v0); // default IO mapping
gl_Position = vec4( (p0.xy - offset) * p0.w / screenSize, p0.z, p0.w);
EmitVertex();
sg_MapIO(v1); // default IO mapping
gl_Position = vec4( (p1.xy + offset) * p1.w / screenSize, p1.z, p1.w);
EmitVertex();
sg_MapIO(v1); // default IO mapping
gl_Position = vec4( (p1.xy - offset) * p1.w / screenSize, p1.z, p1.w);
EmitVertex();
EndPrimitive();
}
\ No newline at end of file
......@@ -3,6 +3,7 @@
in vec3 outGeometryBary;
uniform float lineWidth;
uniform vec4 bkColor;
float edgeFactor()
{
......@@ -18,7 +19,7 @@ void main()
{
SG_FRAGMENT_BEGIN
sg_cColor.xyz = mix(vec3(0.0), sg_cColor.xyz, 1-edgeFactor());
sg_cColor.xyz = mix(bkColor, sg_cColor.xyz, 1-edgeFactor());
SG_FRAGMENT_END
}
\ No newline at end of file
// modified wireframe/hiddenline shader that works for n-poly faces
// template file for shader generator
in float outGeometryHidden;
uniform vec4 bkColor;
void main()
{
SG_FRAGMENT_BEGIN
sg_cColor.xyz = mix(sg_cColor.xyz, bkColor.xyz, outGeometryHidden);
SG_FRAGMENT_END
}
\ No newline at end of file
// modified wireframe/hiddenline shader that works for n-poly faces
// template file for shader generator
in vec3 outGeometryBary;
in vec3 outGeometryPolyEdge;
uniform float lineWidth;
uniform vec4 bkColor;
void main()
{
SG_FRAGMENT_BEGIN
// compute smoothed barycentric distance to edge
vec3 d = fwidth(outGeometryBary);
vec3 a3 = smoothstep(vec3(0.0), d*lineWidth, outGeometryBary);
// edge factor
float t = 1.0;
// barycentric coordinate = 0 <-> pixel located on opposite edge
// check if opposite edge should be hidden or visible
// distance v0 to edge v1-v2
if (outGeometryPolyEdge.y > 0.5)
t = min(t, a3.x);
// distance v1 to edge v2-v0
if (outGeometryPolyEdge.z > 0.5)
t = min(t, a3.y);
// distance v2 to edge v0-v1
if (outGeometryPolyEdge.x > 0.5)
t = min(t, a3.z);
vec3 dbg = vec3(0,0,0);
/*
if (outGeometryPolyEdge.y < 0.5)
dbg.x = min(dbg.x, a3.x);
// distance v1 to edge v2-v0
if (outGeometryPolyEdge.z > 0.5)
dbg.y = min(dbg.y, a3.y);
// distance v2 to edge v0-v1
if (outGeometryPolyEdge.x > 0.5)
dbg.z = min(dbg.z, a3.z);
*/
if (outGeometryPolyEdge.y < 0.5 && outGeometryPolyEdge.z < 0.5)
dbg.x = min(1, a3.x);
dbg.y = dbg.z = dbg.x;
// distance from vertices
/*
if (a3.x < 0.2 && a3.y < 0.02)
t = min(t,a3.z);
if (a3.x < 0.2 && a3.z < 0.02)
t = min(t,a3.y);
if (a3.y < 0.2 && a3.z < 0.02)
t = min(t,a3.x);
*/
t = 1 - t; // clamp(t, 0.0, 1.0);
sg_cColor.xyz = mix(bkColor.xyz, sg_cColor.xyz, t);
sg_cColor.w = 1;
// sg_cColor.xyz = dbg;
SG_FRAGMENT_END
}
\ No newline at end of file
// modified wireframe/hiddenline shader that works for n-poly faces
// template file for shader generator
void main()
{
SG_FRAGMENT_BEGIN
SG_FRAGMENT_END
}
\ No newline at end of file
// modified wireframe/hiddenline shader that works for n-poly faces
// template file for shader generator
in vec3 outGeometryBary;
in vec3 outGeometryPolyEdge;
uniform float lineWidth;
void main()
{
SG_FRAGMENT_BEGIN
// compute smoothed barycentric distance to edge
vec3 d = fwidth(outGeometryBary);
vec3 a3 = smoothstep(vec3(0.0), d*lineWidth, outGeometryBary);
// edge factor
float t = 1.0;
// barycentric coordinate = 0 <-> pixel located on opposite edge
// check if opposite edge should be hidden or visible
// distance v0 to edge v1-v2
if (outGeometryPolyEdge.y > 0.5)
t = min(t, a3.x);
// distance v1 to edge v2-v0
if (outGeometryPolyEdge.z > 0.5)
t = min(t, a3.y);
// distance v2 to edge v0-v1
if (outGeometryPolyEdge.x > 0.5)
t = min(t, a3.z);
t = 1 - t; // clamp(t, 0.0, 1.0);
// use alpha blending to fade out
sg_cColor.a = t;
// discard pixels inside face
if (t < 0.1)
discard;
SG_FRAGMENT_END
}
\ No newline at end of file
// template file for shader generator
in vec3 outGeometryBary;
uniform float lineWidth;
float edgeFactor()
{
// compute smoothed distance to edge, source:
// http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/
vec3 d = fwidth(outGeometryBary);
vec3 a3 = smoothstep(vec3(0.0), d*lineWidth, outGeometryBary);
return min(min(a3.x, a3.y), a3.z);
}
void main()
{
SG_FRAGMENT_BEGIN
float t = 1-edgeFactor();
// use alpha blending to fade out
sg_cColor.a = t;
// without alpha blending:
// sg_cColor.xyz *= t; //mix(vec3(0.0), sg_cColor.xyz, t);
// discard pixels inside face
if (t < 0.1)
discard;
SG_FRAGMENT_END
}
\ No newline at end of file
// template file for shader generator
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
// send barycentric coords to fragment
out vec3 outGeometryBary;
void main()
{
sg_MapIO(0); // IO mapping provided by ShaderGen, send vertex 0
outGeometryBary = vec3(1, 0, 0);
EmitVertex();
sg_MapIO(1); // IO mapping provided by ShaderGen, send vertex 1
outGeometryBary = vec3(0, 1, 0);
EmitVertex();
sg_MapIO(2); // IO mapping provided by ShaderGen, send vertex 2
outGeometryBary = vec3(0, 0, 1);
EmitVertex();
EndPrimitive();
}
\ No newline at end of file
// modified wireframe/hiddenline shader that works for n-poly faces
// template file for shader generator
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
// stores which triangle edge is a poly edge
uniform usamplerBuffer polyEdgeBuffer;
// send barycentric coords to fragment
out vec3 outGeometryBary;
// mark outer/inner edges of the n-poly triangulation
out vec3 outGeometryPolyEdge;
void main()
{
int polyEdge = int(texelFetch(polyEdgeBuffer, gl_PrimitiveIDIn/2).x);
// Each texel in the poly-edge buffer stores edge visibility for two triangles.
// Address the correct bits in the fetched byte:
int offsetBits = (gl_PrimitiveIDIn&1) * 3;
float e01 = 0;
float e12 = 0;
float e20 = 0;
// determine visible/hidden edges:
if ((polyEdge & (1 << offsetBits)) != 0) e01 = 1;
if ((polyEdge & (2 << offsetBits)) != 0) e12 = 1;
if ((polyEdge & (4 << offsetBits)) != 0) e20 = 1;
outGeometryPolyEdge = vec3(e01, e12, e20);
sg_MapIO(0); // IO mapping provided by ShaderGen, send vertex 0
outGeometryBary = vec3(1, 0, 0);
EmitVertex();
sg_MapIO(1); // IO mapping provided by ShaderGen, send vertex 1
outGeometryBary = vec3(0, 1, 0);
EmitVertex();
sg_MapIO(2); // IO mapping provided by ShaderGen, send vertex 2
outGeometryBary = vec3(0, 0, 1);
EmitVertex();
EndPrimitive();
}
// modified hiddenline shader that works for n-poly faces
layout(triangles) in;
layout(triangle_strip, max_vertices = 15) out;
// stores which triangle edge is a poly edge
// i.e. visible edges of a triangulation
uniform usamplerBuffer polyEdgeBuffer;
uniform float lineWidth;
uniform vec2 screenSize;
out float outGeometryHidden;
#include "createEdgeQuad.glsl"
void main()
{
int polyEdge = int(texelFetch(polyEdgeBuffer, gl_PrimitiveIDIn/2).x);
// Each texel in the poly-edge buffer stores edge visibility for two triangles.
// Address the correct bits in the fetched byte:
int offsetBits = (gl_PrimitiveIDIn&1) * 3;
// draw hidden triangle with background color
outGeometryHidden = 1.0;
sg_MapIO(0);
EmitVertex();
sg_MapIO(1);
EmitVertex();
sg_MapIO(2);
EmitVertex();
EndPrimitive();
// draw edges
outGeometryHidden = 0.0;
// determine visible/hidden edges
if ((polyEdge & (1 << offsetBits)) != 0)
createEdgeQuad(0,1, screenSize, lineWidth);
if ((polyEdge & (2 << offsetBits)) != 0)