Hemeshチュートリアル その2 (ControlP5とP3D, PeasyCAMを同時に使うには)

#何を書くか
HemeshをProcessing3で使用する際に、特にControlP5とP3D, PeasyCAMを同時に使う点でぶつかった問題を解決するまでを書きます。

#sampleプログラムレンダリングテスト
```A_key_interface_01.pde
/*
PLEASE READ INTRO TAB
 */

/////////////////////////// GLOBALS ////////////////////////////
import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.hemesh.*;
import wblut.geom.*;

import processing.opengl.*;
import peasy.*;

PeasyCam CAM;
int VIEW;

HE_Mesh MESH;
WB_Render RENDER;
/////////////////////////// SETUP ////////////////////////////

void setup() {
  size(800, 600, OPENGL);
  background(255);
  smooth();
  noStroke();
  VIEW = 1;

  // Our creator and it's params.
  HEC_Dodecahedron creator = new HEC_Dodecahedron();
  creator.setEdge(64);
  creator.setCenter(0, 0, 0).setZAxis(1, 1, 1).setZAngle(PI/4);

  // Here we create a modifier and initialise it 
  HEM_Wireframe strut = new HEM_Wireframe();

  //Parameters for each method of the modifier class can be set seperately ...
  strut.setStrutRadius(50);

  // This method will affine the structures between the connections
  strut.setFidget(2.3);

  //... or all together on one line
  strut.setStrutFacets(6).setMaximumStrutOffset(20);

  // IMPORTANT
  // We first add the creator object to the mesh before modifiying it !
  MESH = new HE_Mesh(creator);

  // and then we add our modifer to our mesh using the modify method
  MESH.modify( strut );  

  RENDER = new WB_Render(this);
  CAM = new PeasyCam(this, 300);
}

/////////////////////////// DRAW ////////////////////////////
void draw() {
  background(255);
  lights();
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  fill(0, 0, 255);

  renderMESH();
}

/////////////////////////// FUNCTIONS ////////////////////////////
void renderMESH() {

  // RENDER is our Render class object we call and to which we add 
  // our mesh. The render class has various methods for drawing to the screen
  switch(VIEW) {
  case 1:
    noStroke();
    RENDER.drawFaces( MESH );
    break;

  case 2:  
    // Nice method for smoothing !
    noStroke();
    RENDER.drawFacesSmooth( MESH );
    break;
  case 3: 
    stroke(255, 0, 0);
    strokeWeight(0.5);
    RENDER.drawEdges( MESH );
    break;

    // AND SOME MORE METHODS OF THE RENDER CLASS
  case 4: 
    // Method for showing the normals
    stroke(0, 255, 0);
    RENDER.drawFaces( MESH );
    RENDER.drawFaceNormals( 20, MESH ); // first param is the length
    break;

  case 5: 
    // method for showing halfedges of the mesh (debugging)
    stroke(0, 255, 0);
    RENDER.drawHalfedges( 20, MESH );
    break;

  case 6: 
    // method for showing halfedges of the mesh (debugging)
    stroke(0, 255, 0);
    fill(255, 0, 0);
    RENDER.drawFaces( MESH );
    RENDER.drawVertexNormals(30, MESH );
  }
}
```

```Key_interaction.pde
/*

We switch from different views with the following keys :
f = show fill / faces
s = smooth fill / faces
e = show only contour / edges
n = show normals
h/t = show half edges

Other stuff
c = png export
m = export mesh stl
o = reset camera position
*/

void keyReleased() {
  if (key == 'f') {
    VIEW = 1;
  }
  if (key == 's') {
    VIEW = 2; //error...
  }
  if (key == 'e') {
    VIEW = 3;
  }
  if (key == 'n') {
    VIEW = 4;
  }
  if (key == 'h') {
    VIEW = 5;
  }
    if (key == 't') {
    VIEW = 6; //error
  }
}

void keyPressed() {

  if (key == 'm') {
    // Hemesh includes a method for exporting geometry
    // in stl file format wich is very handy for 3D printing ;–)
    MESH.triangulate();
    HET_Export.saveToSTL(MESH, sketchPath("export.stl"), "test");
  }
  
   if (key == 'c') {
    saveFrame("screenShot_###.png");
    println("screen shot taken");
   }
  if (key == 'o') {
    // reset camera origin positions  - do this before
    // exporting your shape so your shape is positioned
    // on a flat plane ready for 3D printing
    CAM.reset(1000);
  }
  // Print camera position - could be helpful
  if (key == 'p') {
    float[] camPos = CAM.getPosition();
    println(camPos);
  }
}
```

実行結果
<img width="364" alt="スクリーンショット 2016-08-11 23.29.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/e218df8a-84e2-0fba-91b8-111a3da19425.png">
<img width="368" alt="スクリーンショット 2016-08-11 23.29.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/38ebfce6-0034-d56c-7175-e0f5e32bff6b.png">
<img width="483" alt="スクリーンショット 2016-08-11 23.29.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/f88a6e57-d36e-6285-aa1a-49cdca3a9d39.png">

RENDER.drawFacesSmooth( MESH );
と
RENDER.drawVertexNormals(30, MESH );
で NullPointerExceptionのエラーが出た。未解決。

この最初のやつをstlに書き出したところ、面が完全でない穴ぼこの以下のようなモデルになってしまった。

<img width="491" alt="スクリーンショット 2016-08-11 23.31.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/a38fcb40-f822-358c-eb3f-240b8e8a2af5.png">

#Controlp5利用
##発生した問題
Controlp5を使ったインタラクションを試そうと思ったら、setWindowがもう(processing3では)使えないよと言われた。
代わりにControlFrameを使うべしという指摘が[ここ](https://forum.processing.org/two/discussion/5322/problem-with-controlwindow)にあった。

取り敢えず何も考えずにsetWindowをコメントアウトすると
<img width="394" alt="スクリーンショット 2016-08-12 19.14.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/2ecd8535-aaf1-e873-1a86-9e0a4c8d2c7c.png">
こんな感じで3Dオブジェクトと一緒に動くスライダーになってしまった。
controlP5のGUIオブジェクトを3Dオブジェクトとは別に描画ってどうやるんだろう。

-> 調べてみると、[ここ](http://www.sojamo.de/libraries/archive/controlP5-0-3-14/reference/controlp5_method_setautodraw.htm)にあるようにcontrolp5.draw(); (この場合はINTERFACES.draw();)で描画した方がいいようなのでそうしてみる。
-> 変わらない。

ここでCAMが原因なんじゃないかと思い、CAM関係をコメントアウトすると
<img width="307" alt="スクリーンショット 2016-08-12 23.08.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/4bb4e29d-4f5c-68e5-80a5-3baf25fc5a2e.png">
こうなった。やっぱCAMや。PeasyCAM難しいなぁ。

#PeasyCAMとControlp5を同時に使う時
結論を書くと、
CAM.beginHUD(); と CAM.endHUD();の間にINTERFACES.draw();を実行すればOK.
<img width="350" alt="スクリーンショット 2016-08-12 23.18.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/2bce80c3-7e0b-3b0b-5fb1-0a4c924e98eb.png">

できた！
しかしなんだか超重いな。

というわけで、processing3に対応するように書き換えたサンプルコードが以下

```A_CP5_interface.pde
/*
PLEASE READ INTRO TAB
 */

/////////////////////////// GLOBALS ////////////////////////////
// LIBRARY
import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.hemesh.*;
import wblut.geom.*;
//import processing.opengl.*;

// HEMESH CLASSES & OBJECTS
HE_Mesh MESH; // Our mesh object
WB_Render RENDER; // Our render object

// CAM
import peasy.*;
PeasyCam CAM;

//ControlP5
import controlP5.*;
//Declare our various CP5 objects & variables
ControlP5 INTERFACES;
//ControlWindow CW;
int CHAMFERDIST, CHAMFEREDGE; 

/////////////////////////// SETUP ////////////////////////////

void setup() {
  size(800, 600, P3D);
  controlInit();
  CAM = new PeasyCam(this, 200);
  createMesh();
  createModifiers();
}

/////////////////////////// DRAW ////////////////////////////
void draw() {
  background(255);
  //CAMERA
  CAM.beginHUD(); // this method disables PeasyCam for the commands between beginHUD & endHUD
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  INTERFACES.draw();
  CAM.endHUD();

  //HEMESH
  noStroke();
  fill(#FA890F);
  createMesh();
  createModifiers();
  RENDER.drawFaces( MESH ); // Draw MESH faces
}


// SOME KEYS INTERACTION
void keyPressed() {

  if (key == 'e') {
    // Hemesh includes a method for exporting geometry
    // in stl file format wich is very handy for 3D printing ;–)
    CAM.reset(1000);
    MESH.triangulate();
    HET_Export.saveToSTL(MESH, sketchPath("export.stl"), "test");
  }

  if (key == 's') {
    saveFrame("screenShot_###.png");
    println("screen shot taken");
  }
  if (key == 'o') {
    // reset camera origin positions  - do this before
    // exporting your shape so your shape is positioned
    // on a flat plane ready for 3D printing
    CAM.reset(1000);
  }
  // Print camera position - could be helpful
  if (key == 'p') {
    float[] camPos = CAM.getPosition();
    println(camPos);
  }
}
```

```INTERFACE.pde
////////////////// ControlP5 INTERFACE

void controlInit() {
  INTERFACES = new ControlP5(this);
  INTERFACES.setAutoDraw(false);
  // Create a new window (doesn't work in Processing 2.0+)
  //CW = INTERFACES.addControlWindow("controlP5window", 10, 10, 200, 300, 30);

  //Create sliders
  noLights();
  INTERFACES.addSlider("CHAMFERDIST").setPosition(10, 20).setRange(1, 50).setValue(10); 
  //chamfDist.getCaptionLabel().setColorBackground(color(255, 20, 30)); 
  //chamfDist.getCaptionLabel().getStyle().setPadding(4, 2, 2, 2); 
  //shift the caption label up by 4px
  //chamfDist.getCaptionLabel().getStyle().setMargin(-5, 0, 0, 0); 
  
  INTERFACES.addSlider("CHAMFEREDGE").setPosition(10, 40).setRange(1, 20).setValue(5); 

  // Add elements to new window
  //chamfDist.setWindow(CW);
  //chamfEdge.setWindow(CW);
  INTERFACES.draw();
}
```