Posts Tagged ‘mesh’

QuickLook generator plugin for 3D model mesh file formats (.off, .obj, .mesh, .wrl)

Sunday, October 6th, 2013

Using libigl and Mesa3D’s off screen rendering is was easy to whip up a QuickLook preview and thumbnail generator for our favorite 3D model file formats. I opted to avoid Xcode and compile the application using a good old Makefile. You can find the project in the libigl examples libigl/examples/quicklook-mesh/ (as of version 0.3.2).

Once installed the QuickLook generator will make (static) previews of any .mesh, .off, .obj or .wrl files when hit [space] after selecting the file in Finder.app.

Or when you use one of the fancier Finder.app views

I imagine I’ll be perfecting this over time, but for now it shows 6 canonical views and uses a double-sided material to give you an idea of any inconsistent mesh orientation issues. It should work for triangle meshes or general polygonal meshes.

You can skip compiling and just download Mesh.qlgenerator. Install it by moving Mesh.qlgenerator/ into /Library/QuickLook/. Then either restart you computer or issue in a Terminal:

qlmanage -r
qlmanage -r cache


Update: I’ve finally fixed some issues with the dynamic libraries. The updated Mesh.qlgenerator now contains a binary which should work anywhere, without requiring you to install Mesa3D or any other dependencies.

Update: I’ve moved this out of the libigl/examples to its own repo on github.

Compile SVR on mac os x

Saturday, August 24th, 2013

I managed to get the PLC meshing program SVR (an alternative to tetgen). It was a little difficult to get compiled on Mac OS X so here are my notes.

The first weird thing is that the make configuration for SVR tries to download and compile the boost libraries. To get rid of this and just use boost as installed from macports I commented out the following line in boost/Makefile.in from:

all: stamp-boost


to

all:


Then I ran:

./configure --prefix=/usr/local/svr CPPFLAGS='-Wno-deprecated -fno-strict-aliasing'


Before making there are a few files that need to be altered. In utilities/details/hash.h add the following include:

#include <boost/cstdint.hpp>


In feature-refine/GenericMesh.h, feature-refine/RefineVertex.h, and utilities/FastHash.h remove the following

: public boost::noncopyable


In feature-refine/SVR.cpp, front-end/SVR.cpp, and point-refine/svr.cpp add the following after the definition of valgrind_hack:

((void)valgrind_hack);


Using patcht to texture map a triangle mesh in matlab

Friday, August 9th, 2013

Recently I found the patcht script which lets you texture map a triangle mesh in matlab.

It unfortunately does it in a brute force way: creating a textured surface for every triangle. But it’s at least something. Here’s how I use it for 2d meshes of images using the xy positions as texture coordinates:


patcht(F,V,F,[max(V(:,2))-V(:,2) V(:,1)],im);
axis equal


which produces:

Visualizing samples on a sphere

Friday, May 17th, 2013

In my project, I need to uniformly sample directions or equivalently points on the unit sphere. A correct way is to sample the azimuth and cosine of the polar angle uniformly. Another way is to sample points in ℝ3 randomly with mean at the origin and variance 1.

One naive non uniform way, is to sample the x, y and z between [-1,1] uniformly and normalize. I knew that this was biased, but I wanted to see the bias. Here’s a small matlab program that continuous splats random points onto a textured sphere:


% Can't use sphere(); have to use funny parameterization so tex-mapping works
n = 100;
theta = (-n:2:n)/n*pi;
sinphi = (-n:2:n)'/n*1;
cosphi = sqrt(1-sinphi.^2); cosphi(1) = 0; cosphi(n+1) = 0;
sintheta = sin(theta); sintheta(1) = 0; sintheta(n+1) = 0;
X = cosphi*cos(theta);
Y = cosphi*sintheta;
Z = sinphi*ones(1,n+1);
% square texture resolution
w = 1000;
im = ones(w,w);
s = surf(X,Y,Z,'FaceColor','texturemap','Cdata',im,'EdgeColor','none');
axis equal;
colormap(gray(255));
method = 'naive';
%method = 'uniform';
%method = 'normal-deviation';
it = 0;
sam = 2000000;
while true
switch method
case 'naive'
% uniformly random 3d point, each coord in [-1,1] and normalize
N = normalizerow(2*rand(sam,3)-1);
case 'normal-deviation'
N = normalizerow(normrnd(zeros(sam,3),1));
case 'uniform'
% random polar angle and azimuth
Z = rand(sam,1)*2-1;
A = rand(sam,1)*2*pi;
R =  sqrt(1-Z.^2);
N = [Z R.*cos(A) R.*sin(A)];
end
% project
S = [(N(:,3)+1)/2 (atan2(N(:,2),N(:,1))+pi)/(2*pi)];
% splat!
S = round([mod(w*S(:,2),w-0.5) mod(w*S(:,1),w-0.5)]+1);
im = im + full(1-sparse(S(:,2),S(:,1),1,w,w));
set(s,'CData',matrixnormalize(im));
it = it + 1;
drawnow;
end


This produces an incrementally improving image of a textured sphere where black means high probability and white means low probability:

When this converges we can examine the bias around the sphere:

The “converged” texture map looks like:

Of course, a correct uniform sampling converges rather boringly to a uniformly black sphere.

In a different project we need not only a random sampling on the sphere, but also a Delaunay triangle mesh. Since all points lie on the sphere they also lie on their respective convex hull:


V = normalizerow(normrnd(zeros(sam,3),1));
F = convhulln(V);


The result looks something like this:

OBJ reader, mesh viewer in browser with WebGL

Wednesday, April 10th, 2013

I’m getting around to release source code and demos for my PhD projects and I thought it would be cool to try out writing those demos using WebGL. After toying around (for a few years now), I have a little proof of concept.

This little mesh viewer can load an .obj mesh file (just bare basics of the format) from (1) the remote server, (2) a selected file, or (3) a drag-and-drop file. Just drag an .obj file from your folder onto the viewer! For now there’s not much interface, just a simple zoom mechanism. But I’m going to build on this and keep updated the page.

The source code is heavily borrowed from around the web. See the .js sources.

Bounded biharmonic weights c++ demo for surfaces/volumes in 3d

Monday, December 24th, 2012

By popular demand I’ve created a quick-and-dirty 3d C++ demo of our paper “Bounded Biharmonic Weights for Real-time Deformation” [Jacobson et al. 2011].

The demo takes a mesh (.obj or .off) and a description of a skeleton (in my own ridiculous .bf and .tgf file formats) and computes a tetrahedral volume mesh using tetgen (saved to a .mesh file) and then computed bounded biharmonic weights for each handle over this mesh (save to my not-so-ridiculous .dmat file format).

This is a small program that illustrates how to compute Bounded Biharmonic Weights (BBW) for a given surface mesh and skeleton description. This only supports point handles and skeleton trees (bones). Cages are not supported. This program only computes and outputs the weight function values. You should be able to load them into matlab or maya or whatever.

COMPILE

First install the dependencies (e.g. mosek). If your on a mac, and why wouldn’t you be, then you *should* with any luck and by the grace of God be able to compile with:


make


RUN

Then run an example with (e.g.):


./bbw_demo examples/brick.obj examples/brick.tgf


This will produce at least 2 files:


examples/brick-volume.mesh  Tetrahedral mesh over which weights were computed
examples/brick-volume.dmat  Coefficients of weights defined over .mesh


File Formats

See file-formats/index.html

The dependencies are:

  igl_lib
eigen3
tetgen
mosek

Igl lib

IGL_lib is our groups internal library. It will someday be publicly available, but not yet. Thus I only include here a bare minimum to get this demo to work. The library functions as an inlined header library. All the source is located in ./igl_lib/include/igl

Eigen3: The igl dependencies rely on a depracted version of the Sparse Matrix suite in eigen 3. Thus you need the correct version of eigen. To make this easier I have included the necessary version of eigen here: ./eigen3

Tetgen Tetgen is also free and easy to install. Be sure to compile the library version, i.e.:


make tetlib


To make life a little easier I include a working version of tetgen in ./igl_lib/external/tetgen/. You can go there and compile libtet.a or download the tetgen source and do that on your own. Whatever.

You can try to contact me with questions. Especially regarding the algorithm described in “Bounded biharmonic weights for real-time deformation” by [Jacobson et al. 2011]. However, please
try to solve compilation troubles on your own. I didn’t intend for this code to be machine independent or even necessarily easy to compile. This is a quick and dirty demo because so many people requested it.

Sometime in the not too distant future I still plan to release a *nice* 3d, C++ demo.

Update: I have updated this demo to use the libigl library. Please find the new version on the bbw project page.

Porting Marco Attene’s meshfix to mac os x

Wednesday, November 14th, 2012

Short notes on porting meshfix to mac. Follow readme.txt placing downloaded OpenNL and JMeshLib directories in the mesh fix directory [same as meshfix.cpp] When compiling JMeshLib be sure in makeconf file to change the lines:

# On 64-bit machines you need to uncomment the following line
# -DIS64BITPLATFORM

MOREFLAGS = $(OPTM)$(STRICTALIAS)


to:

MOREFLAGS = $(OPTM)$(STRICTALIAS) -DIS64BITPLATFORM


When compiling JMeshExt I separated the predicates.cxx file from tetgen’s source to make jrs_predicates.h and jrs_predicates.c. Placing them according to readme.txt. One note is that if jrs_predicates.c is to be a .c file rather than a .cpp file then you need to extern "C"{...} encapsulate the jrs_predicates.h header file or you will get Undefined symbol linker errors. Finally in JMeshExt and meshfix.cpp there are several casts of pointers to ints. This won’t compile on 64-bit machines. This similar operation was fixed in JMeshLib by the -DIS64BITPLATFORM. Thus, I’ve fixed the following files to use the j_voidint typedef defined by IS64BITPLATFORM in j_mesh.h:

JMeshExt-1.0alpha_src/src/holeFilling.cpp
meshfix.cpp


I’ve zipped up my changes and organization: download package. Then you can just issue:

cd OpenNL3.2.1/
# # You might need to do:
# export CC=which cc
# export CXX=which c++
./configure.sh
make -C build/Darwin-Release/
cd ../JMeshLib-1.2/
make
cd ../JMeshExt-1.0alpha_src/
make
cd ..
make


I imagine a very similar port could be used for Linux.

Update: I started occasionally getting runtime errors:

OpenNL should not have reached this point: file:/Users/ajx/Downloads/MeshFix/OpenNL3.2.1/src/NL/nl_superlu.c, line:223
Abort trap: 6


Seems this comes from compiling OpenNL without SuperLU support. SuperLU is fairly straightforward to install and so is following the OpenNL instructions for compiling with OpenNL support.

GL_COMPILE_AND_EXECUTE is slow (but apparently everybody knew that)

Thursday, October 11th, 2012

Although it doesn’t seem to be deprecated the glNewList() option GL_COMPILE_AND_EXECUTE is not properly supported by nVidia GPUs. On my linux machine, we installed a fancy pants nVidia card. So I was surprised to find out that my code ran slower there than on my dinky mac. After a long debugging session I found that it was all GL_COMPILE_AND_EXECUTE‘s fault. I was displaying a mesh each frame using code that looked like:


if(!display_list_compiled)
{
dl_id = glGenLists(1);
glNewList(dl_id,GL_COMPILE_AND_EXECUTE);
... //draw mesh
glEndList();
display_list_compiled = true;
}else
{
glCallList(dl_id);
}


I expected that this would be slow for the first time. But in fact it was significantly slower (factor of 100) for every frame! Even though it was using the display list. I guess that passing GL_COMPILE_AND_EXECUTE creates a very badly organized display list and then you’re punished every time you use it.

The solution is of course trivial:


if(!display_list_compiled)
{
dl_id = glGenLists(1);
glNewList(dl_id,GL_COMPILE;
... //draw mesh
glEndList();
display_list_compiled = true;
}
glCallList(dl_id);


but \$#%^&*! what a waste of time.

Technical report: A Cotangent Laplacian for Images as Surfaces

Monday, April 23rd, 2012

We decided to write up a quick, 2 page technical report about some ideas we’ve had in the last year.

Abstract
By embedding images as surfaces in a high dimensional coordinate space defined by each pixel’s Cartesian coordinates and color values, we directly define and employ cotangent-based, discrete differential-geometry operators. These operators define discrete energies useful for image segmentation and colorization.

Isoline plots on triangle meshes in Matlab

Friday, March 9th, 2012

I recently posted how to plot nice looking iso intervals of scalar fields on triangle meshes using matlab. Along with the intervals I gave an ad hoc way of also showing isolines. This works ok if your data has fairly uniform gradient but they’re not really isolines.

Here’s a little matlab function to output real isolines. Save it in a file called isoline.m:


function [LS,LD,I] = isolines(V,F,S,iso)
% ISOLINES compute a list of isolines for a scalar field S defined on the
% mesh (V,F)
%
% [LS,LD,I] = isolines(V,F,S,iso)
%
% Inputs:
%   V  #V by dim list of vertex positions
%   F  #F by 3 list of triangle indices
%   S  #V list of scalar values defined on V
%   iso #iso list of iso values
% Outputs:
%   LS  #L by dim list of isoline start positions
%   LD  #L by dim list of isoline destination positions
%   I  #L list of indices into iso revealing corresponding iso value
%
% alternative: tracing isolines should result in smaller plot data (every
% vertex only appears once
%

% make sure iso is a ROW vector
iso = iso(:)';
% number of isolines
niso = numel(iso);

% number of domain positions
n = size(V,1);
% number of dimensions
dim = size(V,2);

% number of faces
m = size(F,1);

% Rename for convenience
S1 = S(F(:,1),:);
S2 = S(F(:,2),:);
S3 = S(F(:,3),:);

% t12(i,j) reveals parameter t where isovalue j falls on the line from
% corner 1 to corner 2 of face i
t12 = bsxfun(@rdivide,bsxfun(@minus,iso,S1),S2-S1);
t23 = bsxfun(@rdivide,bsxfun(@minus,iso,S2),S3-S2);
t31 = bsxfun(@rdivide,bsxfun(@minus,iso,S3),S1-S3);

% replace values outside [0,1] with NaNs
t12( (t12<-eps)|(t12>(1+eps)) ) = NaN;
t23( (t23<-eps)|(t23>(1+eps)) ) = NaN;
t31( (t31<-eps)|(t31>(1+eps)) ) = NaN;

% masks for line "parallel" to 12
l12 = ~isnan(t23) & ~isnan(t31);
l23 = ~isnan(t31) & ~isnan(t12);
l31 = ~isnan(t12) & ~isnan(t23);

% find non-zeros (lines) from t23 to t31
[F12,I12,~] = find(l12);
[F23,I23,~] = find(l23);
[F31,I31,~] = find(l31);
% indices directly into t23 and t31 corresponding to F12 and I12
%ti12 = sub2ind(size(l12),F12,I12);
%ti23 = sub2ind(size(l23),F23,I23);
%ti31 = sub2ind(size(l31),F31,I31);
% faster sub2ind
ti12 = F12+(I12-1)*size(l12,1);
ti23 = F23+(I23-1)*size(l23,1);
ti31 = F31+(I31-1)*size(l31,1);

% compute actual position values
LS = [ ...
... % average of vertex positions between 2 and 3
bsxfun(@times,1-t23(ti12), V(F(F12,2),:)) + ...
bsxfun(@times,  t23(ti12), V(F(F12,3),:)) ...
; ... % average of vertex positions between 2 and 3
bsxfun(@times,1-t31(ti23), V(F(F23,3),:)) + ...
bsxfun(@times,  t31(ti23), V(F(F23,1),:)) ...
;... % average of vertex positions between 2 and 3
bsxfun(@times,1-t12(ti31), V(F(F31,1),:)) + ...
bsxfun(@times,  t12(ti31), V(F(F31,2),:)) ...
];
LD = [...
... % hcat with average of vertex positions between 3 and 1
bsxfun(@times,1-t31(ti12), V(F(F12,3),:)) + ...
bsxfun(@times,  t31(ti12), V(F(F12,1),:)) ...
;... % hcat with average of vertex positions between 3 and 1
bsxfun(@times,1-t12(ti23), V(F(F23,1),:)) + ...
bsxfun(@times,  t12(ti23), V(F(F23,2),:)) ...
;... % hcat with average of vertex positions between 3 and 1
bsxfun(@times,1-t23(ti31), V(F(F31,2),:)) + ...
bsxfun(@times,  t23(ti31), V(F(F31,3),:)) ...
];

% I is just concatenation of each I12
I = [I12;I23;I31];

end


Then if you have a mesh in (V,F) and so scalar data in S. You can display isolines using:


colormap(jet(nin));
trisurf(F,V(:,1),V(:,2),V(:,3),'CData',S,'FaceColor','interp','FaceLighting','phong','EdgeColor','none');
axis equal;
[LS,LD,I] = isolines(V,F,S,linspace(min(S),max(S),nin+1));
hold on;
plot([LS(:,1) LD(:,1)]',[LS(:,2) LD(:,2)]','k','LineWidth',3);
hold off
set(gcf, 'Color', [1,1,1]);
set(gca, 'visible', 'off');
%O = outline(F);
%hold on;
%plot([V(O(:,1),1) V(O(:,2),1)]',[V(O(:,1),2) V(O(:,2),2)]','-k','LineWidth',2);
%hold off


Uncomment the last lines to display the outline of the mesh. This produces:

If you use the my anti-alias script you can get pretty good results, almost good enough for a camera-ready paper: