Posts Tagged ‘mesh’

OBJ reader, mesh viewer in browser with WebGL

Wednesday, April 10th, 2013

mesh in browser

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

Mosek: Mosek is free for academics and has its own installation instructions. Adjust your makefile accordingly.

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
please
please
please
please
please
please
please
please
please
please
please
please
please
please
please
please
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/
./configure.sh 
cd build/Darwin-Release/
make
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

A Cotangent Laplacian for Images as Surfaces
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:
woody matlab isolines
If you use the my anti-alias script you can get pretty good results, almost good enough for a camera-ready paper:
woody matlab isolines anti-aliased

Get curve sketch (pen tool) from user in MATLAB figure

Thursday, March 1st, 2012

Here’s a little function that asks the user for a curve drawn on the current figure’s current axis. Similar to a pen/pencil tool in Photoshop etc.

Save this in a file called get_pencil_curve.m:


function P = get_pencil_curve(f)
  % GET_PENCIL_CURVE Get a curve (sequence of points) from the user by dragging
  % on the current plot window
  %
  % P = get_pencil_curve()
  % P = get_pencil_curve(f)
  % 
  % Inputs:
  %   f  figure id
  % Outputs:
  %   P  #P by 2 list of point positions
  %
  %

  % Get the input figure or get current one (creates new one if none exist)
  if nargin == 0 || isempty(f)
    f = gcf;
  end
  figure(f);

  % get axes of current figure (creates on if doesn't exist)
  a = gca;

  % set equal axis 
  axis equal;
  % freeze axis
  axis manual;
  % set view to XY plane
  view(2);

  set(gcf,'windowbuttondownfcn',@ondown);
  set(gcf,'keypressfcn',        @onkeypress);
  % living variables
  P = [];
  p = [];

  % loop until mouse up or ESC is pressed
  done = false;
  while(~done)
    drawnow;
  end

  % We've been also gathering Z coordinate, drop it
  P = P(:,1:2);

  % Callback for mouse press
  function ondown(src,ev)
    % Tell window that we'll handle drag and up events
    set(gcf,'windowbuttonmotionfcn', @ondrag);
    set(gcf,'windowbuttonupfcn',     @onup);
    append_current_point();
  end

  % Callback for mouse drag
  function ondrag(src,ev)
    append_current_point();
  end

  % Callback for mouse release
  function onup(src,ev)
    % Tell window to handle down, drag and up events itself
    finish();
  end

  function onkeypress(src,ev)
    % escape character id
    ESC = char(27);
    switch ev.Character
    case ESC
      finish();
    otherwise
      error(['Unknown key: ' ev.Character]);
    end
  end

  function append_current_point()
    % get current mouse position
    cp = get(gca,'currentpoint');
    % append to running list
    P = [P;cp(1,:)];
    if isempty(p)
      % init plot
      hold on;
      p = plot(P(:,1),P(:,2));
      hold off;
    else
      % update plot
      set(p,'Xdata',P(:,1),'Ydata',P(:,2));
    end
  end

  function finish()
    done = true;
    set(gcf,'windowbuttonmotionfcn','');
    set(gcf,'windowbuttonupfcn','');
    set(gcf,'windowbuttondownfcn','');
    set(gcf,'keypressfcn','');
  end

end

Here I’m using the function ask the user to draw a curve (blue) and then immediately simplify it (red), then mesh the interior (green).
matlab mesh sketch

Display wireframe mesh in matlab and save as vector graphics

Saturday, January 14th, 2012

For our siggraph submission I wanted to overlay a 2D image with the wireframe of the mesh we use to deform it. In matlab I can display a 2D mesh (V,F) easily using:


trisurf(F,V(:,1),V(:,2),0*V(:,1));
view(2);
axis equal;

If I save this as .eps then I get a nice vector graphics version of my mesh.

But if I don’t want the filled faces to show up in the vector output, then when I try to save the following figure as a .eps file:


trisurf(F,V(:,1),V(:,2),0*V(:,1),'FaceAlpha',0);
view(2);
axis equal;

matlab just pretends to make a postscript vector file but instead its just a .eps file containing a raster image.

Here’s what I do to get a .eps vector graphics postscript file (which can then be turned into a PDF since output to PDF is broken in matlab):


E = edges(F);
plot([V(E(:,1),1) V(E(:,2),1)]',[V(E(:,1),2) V(E(:,2),2)]','k-');
view(2);
axis equal
set(gcf, 'Color', [1,1,1]);
set(gca, 'visible', 'off');

New ETHZ masters thesis project available: Server-client mesh processing

Sunday, January 1st, 2012

server client mesh processing eth masters project

Olga Sorkine and I will be hosting a master’s thesis project. The project, entitled Server-client mesh processing is now available, and we are eagerly awaiting applications.Many polygon mesh processing techniques are difficult to implement. These techniques often go unseen and unused beyond publication. Others are too computationally intensive for consumer-level laptops, so their use is limited to academic or commercial communities. Fortunately, polygon mesh processing techniques are well-suited for a server-client implementation. Since most techniques need only an input mesh and a few parameters, all computation may be done on a publicly available web server, relying on the client only to upload the input and download the output.

In this project, we will set up the necessary infrastructure on both the server and client end. The client side will be a light-weight, browser-based interface for viewing and uploading 3D surface meshes. The server side will be a clean API to many existing mesh processing implementations (e.g. smooth- ing, curvature computation, tetrahedral volume meshing). With this infrastructure, we will explore geometric data compression specific to each flavor of mesh processing in order to optimize the transfer of output data. We may also use this infrastructure to conduct large scale user-studies via Amazon’s Mechanical Turk. Such large scale user studies are rarely achieved in the mesh processing community. A server-client infrastructure should finally make human evaluation possible. Please don’t hesitate to contact me for more details.

Also, check out the full list of IGL projects.

Note: You will need to be at an ETH IP address to visit these links.

Perceived stretch from partial view of twist

Wednesday, August 31st, 2011

Here’s a twist applied to a bar.

But when the movie is cropped the viewer gets a sense that the bar is also being stretched, to the right.

Putting a texture (here just a the edges of the 3D model’s mesh), helps perceive the true deformation: twist.

It also helps reduce the perceived stretch effect on the original full-view movie.