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