1 /****************************************************************************
2 * MeshLab o o *
3 * A versatile mesh processing toolbox o o *
4 * _ O _ *
5 * Copyright(C) 2005 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23
24 #include "DominancyClassifier.h"
25 #include <wrap/gl/shot.h>
26 #include <common/pluginmanager.h>
27 #include "floatbuffer.h"
28 #include "utils.h"
29
30
31
32
DominancyClassifier(CMeshO & mesh,QList<OOCRaster> & rasterList,int weightMask)33 DominancyClassifier::DominancyClassifier( CMeshO &mesh, QList<OOCRaster> &rasterList, int weightMask ) :
34 m_Mesh(mesh),
35 m_RasterList(rasterList),
36 m_VertexDom(mesh.vn),
37 m_WeightMask(weightMask)
38 {
39 updateDepthRange();
40 updateMeshVBO();
41 initShaders();
42
43 for( QList<OOCRaster>::iterator r=rasterList.begin(); r!=rasterList.end(); ++r )
44 {
45 projectiveTexMatrices( *r );
46 setupShadowTexture( *r );
47 generateWeightsAndShadowMap( *r );
48 checkDominancy( *r );
49 }
50
51 releaseAll();
52 }
53
54
initShaders()55 bool DominancyClassifier::initShaders()
56 {
57 std::string logs;
58 if( !loadShader(m_WeightShader ,"weight_gen" ,&logs) ||
59 !loadShader(m_VisCheckShader,"visibility_check",&logs) )
60 {
61 qWarning( (__FUNCTION__": "+logs).c_str() );
62 return false;
63 }
64
65
66 GLint useImageBorderMask = m_WeightMask & W_IMG_BORDER;
67 GLint useViewDirectionMask = m_WeightMask & W_ORIENTATION;
68 GLint useDistanceMask = m_WeightMask & W_DISTANCE;
69 GLint useSilhouetteMask = m_WeightMask & W_SILHOUETTE;
70
71 m_WeightShader.SetUniform( "u_UseImageBorderMask" , &useImageBorderMask );
72 m_WeightShader.SetUniform( "u_UseViewDirectionMask", &useViewDirectionMask );
73 m_WeightShader.SetUniform( "u_UseDistanceMask" , &useDistanceMask );
74 m_WeightShader.SetUniform( "u_DepthMin" , &m_DepthMin );
75 m_WeightShader.SetUniform( "u_DepthMax" , &m_DepthMax );
76
77 m_VisCheckShader.SetUniform( "u_UseSilhouetteMask" , &useSilhouetteMask );
78
79 return true;
80 }
81
82
updateMeshVBO()83 void DominancyClassifier::updateMeshVBO()
84 {
85 #pragma pack(push,1)
86 struct VBOData
87 {
88 GLfloat vertex[3];
89 GLfloat normal[3];
90 GLint id;
91 };
92 #pragma pack(pop)
93
94
95 // Gathers vertex position and normal data from the mesh, and store them on GPU into a VBO.
96 VBOData *vboData = new VBOData [ m_Mesh.vn ];
97
98 for( int v=0; v<m_Mesh.vn; ++v )
99 {
100 for( int i=0; i<3; ++i )
101 {
102 vboData[v].vertex[i] = m_Mesh.vert[v].P()[i];
103 vboData[v].normal[i] = m_Mesh.vert[v].N()[i];
104 }
105 vboData[v].id = v;
106 }
107
108 m_MeshVBO.Create( GL_STATIC_DRAW, vboData, m_Mesh.vn );
109 m_MeshVBO.Vertex.SetPointer( sizeof(VBOData), OffsetOf(VBOData,vertex) );
110 m_MeshVBO.Normal.SetPointer( sizeof(VBOData), OffsetOf(VBOData,normal) );
111 m_MeshVBO.TexCoord.SetPointer( sizeof(VBOData), OffsetOf(VBOData,id) );
112
113 delete [] vboData;
114
115
116 // Gathers mesh face indices and store them into the VBO.
117 unsigned int *vboIndices = new unsigned int [ 3*m_Mesh.fn ];
118
119 for( int f=0, n=0; f<m_Mesh.fn; ++f )
120 for( int i=0; i<3; ++i, ++n )
121 vboIndices[n] = m_Mesh.face[f].V(i) - &m_Mesh.vert[0];
122
123 m_MeshVBO.LoadIndices( GL_STATIC_DRAW, vboIndices, 3*m_Mesh.fn );
124
125 delete [] vboIndices;
126 }
127
128
updateDepthRange()129 void DominancyClassifier::updateDepthRange()
130 {
131 m_DepthMin = std::numeric_limits<float>::max();
132 m_DepthMax = -std::numeric_limits<float>::max();
133
134 for( QList<OOCRaster>::iterator r=m_RasterList.begin(); r!=m_RasterList.end(); ++r )
135 {
136 float zNear, zFar;
137 GlShot< vcg::Shot<float> >::GetNearFarPlanes( r->shot, m_Mesh.bbox, zNear, zFar );
138
139 if( zNear < m_DepthMin )
140 m_DepthMin = zNear;
141 if( zFar > m_DepthMax )
142 m_DepthMax = zFar;
143 }
144
145 if( m_DepthMin < 0.0001f )
146 m_DepthMin = 0.1f;
147 if( m_DepthMax < m_DepthMin )
148 m_DepthMax = m_DepthMin + 1000.0f;
149 }
150
151
projectiveTexMatrices(OOCRaster & rr)152 void DominancyClassifier::projectiveTexMatrices( OOCRaster &rr )
153 {
154 // Recover the view frustum of the current raster.
155 float zNear, zFar;
156 GlShot< vcg::Shot<float> >::GetNearFarPlanes( rr.shot, m_Mesh.bbox, zNear, zFar );
157 if( zNear < 0.0001f )
158 zNear = 0.1f;
159 if( zFar < zNear )
160 zFar = zNear + 1000.0f;
161
162 float l, r, b, t, focal;
163 rr.shot.Intrinsics.GetFrustum( l, r, b, t, focal );
164
165
166 // Compute from the frustum values the camera projection matrix.
167 m_Proj.SetZero();
168 m_Proj[0][0] = 2.0f*focal / (r-l);
169 m_Proj[2][0] = (r+l) / (r-l);
170 m_Proj[1][1] = 2.0f*focal / (t-b);
171 m_Proj[2][1] = (t+b) / (t-b);
172 m_Proj[2][2] = (zNear+zFar) / (zNear-zFar);
173 m_Proj[3][2] = 2.0f*zNear*zFar / (zNear-zFar);
174 m_Proj[2][3] = -1.0f;
175
176
177 // Extract the pose matrix from the current raster.
178 m_Pose = vcg::Transpose( rr.shot.GetWorldToExtrinsicsMatrix() );
179
180
181 // Define the bias matrix that will enable to go from clipping space to texture space.
182 const float biasMatData[16] = { 0.5f, 0.0f, 0.0f, 0.0f,
183 0.0f, 0.5f, 0.0f, 0.0f,
184 0.0f, 0.0f, 0.5f, 0.0f,
185 0.5f, 0.5f, 0.5f, 1.0f };
186
187 m_TexProj = m_Pose * m_Proj * vcg::Matrix44f(biasMatData);
188 }
189
190
setupShadowTexture(OOCRaster & rr)191 void DominancyClassifier::setupShadowTexture( OOCRaster &rr )
192 {
193 const vcg::Point2i vp = rr.shot.Intrinsics.ViewportPx;
194
195 if( !m_ShadowMap.IsInstantiated() ||
196 m_ShadowMap.Width() !=vp.X() ||
197 m_ShadowMap.Height()!=vp.Y() )
198 {
199 glPushAttrib( GL_TEXTURE_BIT );
200
201 // Create and initialize the OpenGL texture object used to store the shadow map.
202 m_ShadowMap.Create( GL_DEPTH_COMPONENT,
203 vp.X(),
204 vp.Y(),
205 GL_DEPTH_COMPONENT,
206 GL_INT,
207 NULL );
208
209 m_ShadowMap.SetFiltering( GL_NEAREST );
210 m_ShadowMap.SetParam( GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE );
211 m_ShadowMap.SetParam( GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
212 m_ShadowMap.SetParam( GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY );
213
214 glPopAttrib();
215 }
216 }
217
218
generateWeightsAndShadowMap(OOCRaster & rr)219 void DominancyClassifier::generateWeightsAndShadowMap( OOCRaster &rr )
220 {
221 // Backup the previous OpenGL states.
222 glPushAttrib( GL_VIEWPORT_BIT |
223 GL_COLOR_BUFFER_BIT |
224 GL_TRANSFORM_BIT |
225 GL_ENABLE_BIT |
226 GL_POLYGON_BIT );
227
228
229 // Set the OpenGL matrices so as to place the viewpoint on the given raster.
230 glMatrixMode( GL_PROJECTION );
231 glPushMatrix();
232 glLoadMatrixf( m_Proj.V() );
233
234 glMatrixMode( GL_MODELVIEW );
235 glPushMatrix();
236 glLoadMatrixf( m_Pose.V() );
237
238
239 // Create the off-screen rendering context, using the shadow map texture as depth buffer.
240 //m_WeightMap.Create( GL_LUMINANCE32F_ARB, m_ShadowMap.Width(), m_ShadowMap.Height() );
241 m_WeightMap.Create( GL_LUMINANCE, m_ShadowMap.Width(), m_ShadowMap.Height() );
242 m_WeightMap.SetFiltering( GL_NEAREST );
243
244 GPU::FrameBuffer fbuffer( m_ShadowMap.Width(), m_ShadowMap.Height() );
245 fbuffer.Attach( GL_COLOR_ATTACHMENT0, m_WeightMap );
246 fbuffer.Attach( GL_DEPTH_ATTACHMENT, m_ShadowMap );
247 fbuffer.Bind();
248
249
250 // Perform the rendering pass that computes the the shadow map used afterward for visibility checks
251 // as well as the first weight map containing the orientation mask, the image border mask and
252 // the distance masks (the silhouette mask is computed hereafter, using the depth gather in the shadow map).
253 glEnable( GL_DEPTH_TEST );
254 glEnable( GL_POLYGON_OFFSET_FILL );
255 glPolygonOffset( 2.0f, 2.0f );
256
257 glClearColor( 0.0f, 1.0f, 0.0f, 1.0f );
258 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
259
260 m_WeightShader.Bind();
261 m_MeshVBO.TexCoord.Disable();
262 m_MeshVBO.Bind();
263 m_MeshVBO.DrawElements( GL_TRIANGLES, 0, 3*m_Mesh.fn );
264 m_MeshVBO.Unbind();
265 m_MeshVBO.TexCoord.Enable();
266 m_WeightShader.Unbind();
267
268 fbuffer.Unbind();
269
270
271 // Restore the previous OpenGL states.
272 glMatrixMode( GL_PROJECTION );
273 glPopMatrix();
274 glMatrixMode( GL_MODELVIEW );
275 glPopMatrix();
276
277 glPopAttrib();
278 #if 0
279 unsigned char *b = new unsigned char [ fbuffer.Width()*fbuffer.Height() ];
280 fbuffer.DumpTo( GL_COLOR_ATTACHMENT0, b, GL_LUMINANCE, GL_UNSIGNED_BYTE );
281 QImage img( b, fbuffer.Width(), fbuffer.Height(), QImage::Format_Indexed8 );
282 img = img.convertToFormat( QImage::Format_RGB888 );
283 img.save( "test1.png" );
284 delete [] b;
285 #endif
286
287
288 // Recover the depth of the scene and create the texture containing the step discontinuity mask, if required.
289 if( m_WeightMask & W_SILHOUETTE )
290 {
291 glPushAttrib( GL_TEXTURE_BIT );
292
293 // Read the depth normalized between 0 and 1 from the framebuffer.
294 floatbuffer buffer1, buffer2;
295 buffer1.init( fbuffer.Width(), fbuffer.Height() );
296 buffer2.init( fbuffer.Width(), fbuffer.Height() );
297 fbuffer.DumpTo( GL_DEPTH_ATTACHMENT, buffer1.data, GL_DEPTH_COMPONENT, GL_FLOAT ); // WARNING: leads to a GL error.
298
299
300 float zNear, zFar;
301 GlShot< vcg::Shot<float> >::GetNearFarPlanes( rr.shot, m_Mesh.bbox, zNear, zFar );
302 if( zNear < 0.0001f )
303 zNear = 0.1f;
304 if( zFar < zNear )
305 zFar = zNear + 1000.0f;
306
307 float range = zFar - zNear;
308 for( unsigned int i=0; i<fbuffer.Width()*fbuffer.Height(); ++i )
309 if( buffer1.data[i] < 1.0f )
310 buffer1.data[i] = zNear*zFar / ((zFar - buffer1.data[i]*range)*range);
311
312
313 // Detect step discontinuities and compute the distance of each pixel to the closest one.
314 buffer2.applysobel( &buffer1 );
315 buffer2.initborder( &buffer1 );
316 buffer2.distancefield();
317
318 // Create the OpenGL texture containing the weight values.
319 //m_WeightMapSilh.Create( GL_LUMINANCE32F_ARB, fbuffer.Width(), fbuffer.Height(), GL_LUMINANCE, GL_FLOAT, buffer2.data );
320 m_WeightMapSilh.Create( GL_LUMINANCE, fbuffer.Width(), fbuffer.Height(), GL_LUMINANCE, GL_FLOAT, buffer2.data );
321 m_WeightMapSilh.SetFiltering( GL_NEAREST );
322
323 glPopAttrib();
324 #if 0
325 unsigned char *b = new unsigned char [ fbuffer.Width()*fbuffer.Height() ];
326 for( unsigned int i=0; i<fbuffer.Width()*fbuffer.Height(); ++i )
327 b[i] = (unsigned char)(255.0f*buffer2.data[i]);
328 QImage img( b, fbuffer.Width(), fbuffer.Height(), QImage::Format_Indexed8 );
329 img = img.convertToFormat( QImage::Format_RGB888 );
330 img.save( "test2.png" );
331 delete [] b;
332 #endif
333 }
334 }
335
336
337 // This function performs visibility check and gathers weights for all mesh vertices with respect to the given raster,
338 // and update their dominant images accordingly.
339 // The computation is done by a shader that stores weight values in the frame buffer, with a negative weight for
340 // vertices that are not visible from the current raster's viewpoint. For each mesh vertex, a pixel is written,
341 // and pixel ordering in the framebuffer corresponds to vertex ordering in the mesh.
checkDominancy(OOCRaster & rr)342 void DominancyClassifier::checkDominancy( OOCRaster &rr )
343 {
344 // Save the previous OpenGL state.
345 glPushAttrib( GL_VIEWPORT_BIT |
346 GL_ENABLE_BIT |
347 GL_TEXTURE_BIT );
348
349
350 // Create a frambuffer with a viewport big enough to define one pixel per mesh vertex.
351 vcg::Point2i vp;
352 vp[0] = 2048;
353 vp[1] = (int) std::ceil( (float)m_Mesh.vn / vp[0] );
354
355 GPU::FrameBuffer fbuffer( vp[0], vp[1] );
356 fbuffer.Attach( GL_COLOR_ATTACHMENT0, GL_LUMINANCE32F_ARB );
357 fbuffer.Bind();
358
359
360 // Set up the shader that will compute visibility check and weight gathering.
361 m_VisCheckShader.Bind();
362 m_ShadowMap.Bind( 0 );
363 m_VisCheckShader.SetSampler( "u_ShadowMap" , 0 );
364 m_WeightMap.Bind( 1 );
365 m_VisCheckShader.SetSampler( "u_WeightMap" , 1 );
366 if( m_WeightMask & W_SILHOUETTE )
367 {
368 m_WeightMapSilh.Bind( 2 );
369 m_VisCheckShader.SetSampler( "u_WeightMapSilh", 2 );
370 }
371 m_VisCheckShader.SetUniform( "u_Viewport", vp.V() );
372 m_VisCheckShader.SetUniform( "u_Viewpoint", rr.shot.GetViewPoint().V() );
373 m_VisCheckShader.SetUniform( "u_ShadowProj", m_TexProj.V() );
374
375
376 // Perform the rendering pass.
377 glDisable( GL_DEPTH_TEST );
378 m_MeshVBO.Bind();
379 m_MeshVBO.DrawArrays( GL_POINTS, 0, m_Mesh.vn );
380 m_MeshVBO.Unbind();
381
382
383 // Restore the previous OpenGL state.
384 m_ShadowMap.Unbind();
385 m_WeightMap.Unbind();
386 if( m_WeightMask & W_SILHOUETTE )
387 m_WeightMapSilh.Unbind();
388 m_VisCheckShader.Unbind();
389
390 fbuffer.Unbind();
391
392 glPopAttrib();
393
394
395 // Read the content of the framebuffer and update the vertex dominancy accordingly.
396 float *weightBuffer = new float [ vp[0]*vp[1] ];
397 fbuffer.DumpTo( GL_COLOR_ATTACHMENT0, weightBuffer, GL_LUMINANCE, GL_FLOAT );
398
399 for( int v=0; v<m_Mesh.vn; ++v )
400 if( weightBuffer[v] > m_VertexDom[v].weight1 )
401 {
402 m_VertexDom[v].weight2 = m_VertexDom[v].weight1;
403 m_VertexDom[v].dominant2 = m_VertexDom[v].dominant1;
404
405 m_VertexDom[v].weight1 = weightBuffer[v];
406 m_VertexDom[v].dominant1 = &rr;
407 }
408 else if( weightBuffer[v] > m_VertexDom[v].weight2 )
409 {
410 m_VertexDom[v].weight2 = weightBuffer[v];
411 m_VertexDom[v].dominant2 = &rr;
412 }
413
414 delete [] weightBuffer;
415 }
416
417
releaseAll()418 void DominancyClassifier::releaseAll()
419 {
420 m_MeshVBO.Release();
421 m_WeightShader.Release();
422 m_WeightMap.Release();
423 m_WeightMapSilh.Release();
424 m_ShadowMap.Release();
425 }
426
427
dominancyCoverage(RasterFaceMap & rpatches) const428 void DominancyClassifier::dominancyCoverage( RasterFaceMap &rpatches ) const
429 {
430 rpatches.clear();
431
432 for( CMeshO::FaceIterator f=m_Mesh.face.begin(); f!=m_Mesh.face.end(); ++f )
433 {
434 const VDominancy &d0 = (*this)[f->V(0)];
435 const VDominancy &d1 = (*this)[f->V(1)];
436 const VDominancy &d2 = (*this)[f->V(2)];
437
438 std::set<OOCRaster*> rastersFBelongsTo;
439
440 if( d0.dominant1 )
441 rastersFBelongsTo.insert( d0.dominant1 );
442 if( d1.dominant1 )
443 rastersFBelongsTo.insert( d1.dominant1 );
444 if( d2.dominant1 )
445 rastersFBelongsTo.insert( d2.dominant1 );
446
447 if( d0.isOnBoundary() )
448 rastersFBelongsTo.insert( d0.dominant2 );
449 if( d1.isOnBoundary() )
450 rastersFBelongsTo.insert( d1.dominant2 );
451 if( d2.isOnBoundary() )
452 rastersFBelongsTo.insert( d2.dominant2 );
453
454 for( std::set<OOCRaster*>::iterator r=rastersFBelongsTo.begin(); r!=rastersFBelongsTo.end(); ++r )
455 rpatches[*r].push_back( &*f );
456 }
457 }
458