ふなまる水産業務作業日報

フロントエンドエンジニアをやらせていただいております。日々のつまづきを備忘録として残していきます。

three.js(version r106)でMMDモデルを動かす

three.js(version r106)でMMDモデルを動かす

概要

公式のexampleはこちら▼
- MMDダンス: https://threejs.org/examples/#webgl_loader_mmd - カメラモーション付き: https://threejs.org/examples/#webgl_loader_mmd_audio

exampleを参考に作ってみました。▼
- https://mmdtest.netlify.com/ (めちゃくちゃ重いので時間かかります。)

github
- https://github.com/kamabokochan/Jasmine

お借りしたもの▼
- ちゃわんむ式善逸モデル(https://www.nicovideo.jp/watch/sm35808552) ちゃわんむし様 - 【MMD戦国BASARA】極楽浄土 モーショントレース(https://www.nicovideo.jp/watch/sm29180863) yurie様 - カメラモーション ゆきねこ様 - ステージ 冴木稲荷神社 シロ様

ソースコード解説

各所ポイントのみ

js

initの中

単純なthree.jsのセッティング部分です(光とかカメラとか)▼

this.scene = new THREE.Scene();
this.clock = new THREE.Clock();
this.setLight();
this.setDisplay();
this.setCamera();
this.bindEvent();
this.loader = new THREE.MMDLoader();

各外部ファイルの読み込みが完了するまでawaitで待機して同期的な処理にしています▼

await this.LoadPMX();
await this.LoadStage();
this.vmd = await this.LoadVMD();

アニメーションの読み込み▼

this.helper = new THREE.MMDAnimationHelper();

    this.helper.add(this.mesh, {
      animation: this.vmd,
      physics: false
    });

アニメーションのループ▼
requestAnimationFrameでループ helper.updateはDelta:numberを受け取ってミキサー時間を進め、ヘルパーに追加されたオブジェクトのアニメーションを更新します

 Render() {
    requestAnimationFrame(() => this.Render());
    this.renderer.clear();
    this.renderer.render(this.scene, this.camera);
    this.helper.update(this.clock.getDelta());
  }

まとめ

動くと感動するね!

JavaScriptの同期処理・非同期処理


※この記事の内容はJavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみるを参考に、自分用にまとめただけなので、こちらのリンクを辿って参考にした方が良いです.


JavaScriptの同期処理・非同期処理

Javascriptはシングルスレッド

  • Javascriptはシングルスレッドで動いている.
  • 同期であろうと非同期であろうと2つ以上の処理を同時に行なうことはできない.
  • JavaScriptでは、キューに登録された関数が順番にひとつずつ実行されていく.
  • でもキューに登録される順番が同期であったり非同期であったりする.

まとめると - JavaScriptは並列処理できない:1度に1つの仕事しかできない。 - JavaScriptは非同期処理できる:誰か(DB等)に仕事を任せている間に自分の他の仕事を進めることができる

同期処理・非同期処理

  • 同期処理
    • 書いた順番に実行されていく.
    • 重たい処理が間にあると、そこで大きな待ち時間が生まれる.

同期処理の例

console.log(1);
console.log(2);
console.log(3);

実行結果

1
2
3
  • 非同期処理
    • あるタスクが実行をしている際に、他のタスクが別の処理を実行できる方式.
    • 例えばサーバーと通信を行った際に、リクエストが返ってくるまでに数秒以上もかかる場合、レスポンスが返るまでに一旦関数から抜けて別の処理を進めて、レスポンスを受け取り次第、呼び出し元に値を返す.

非同期処理の例

console.log(1);
setTimeout(function(){console.log(2)}, 1000);
console.log(3);

キューの登録順

console.log(1);
setTimeout(function(){console.log(2)}, 1000); //★
console.log(3);
// 1000ミリ秒後に★で登録された新たなキュー↓
console.log(2)

実行結果

1
3
2

関数setTimeoutに無名関数function(){console.log(2)}が渡ったように、ある関数Aの引数に別の関数Bを渡し、AからBを呼び出すことをコールバックという。

非同期処理の例2

var xhr = new XMLHttpRequest();
xhr.open('GET','何かのAPI');
xhr.send();
xhr.addEventListener('load', function(result){
  console.log(1);
});
console.log(2);

キューの登録順

xhr.open('GET','何かのAPI');
xhr.send();
xhr.addEventListener('load', function(result){
  console.log(1); // ★
});
console.log(2);
console.log(1); // ★

addEventListener()でloadイベントが発生した時点でlog(1)がキューに登録される. これはその前の一連の関数とは関係ないタイミングでキューに登録されるので非同期.

実行結果

2
1

つまり、JavaScriptXMLHttpRequestを使って外部と通信している間に、次の自分の仕事であるconsole.log(2)を進めている事になる. これにより、JavaScriptのスレッドでやらなくても良い仕事を外部にやらせてる間にJavaScriptのスレッドが自分の仕事を進められる.

同期処理と非同期処理の比較

複数行の処理を行う上で、同期処理は簡潔だが、非同期処理は複雑になる。

複数行の同期処理

関数の例

var syncBuyApple = function(payment){
  if(payment >= 150){
    return {change:payment-150, error:null};
  }else{
    return {change:null, error:150-payment + '円足りません。'};
  }
}

複数行の同期処理

var result1 = syncBuyApple(500);
if(result1.change !== null){
  console.log('1つ目のおつりは' + result1.change + '円です。');
}
if(result1.error !== null){
  console.log('1つ目でエラーが発生しました:' + result1.error);
}
var result2 = syncBuyApple(result1.change);
if(result2.change !== null){
  console.log('2つ目のおつりは' + result2.change + '円です。');
}
if(result2.error !== null){
  console.log('2つ目でエラーが発生しました:' + result2.error);
}
var result3 = syncBuyApple(result2.change);
if(result3.change !== null){
  console.log('3つ目のおつりは' + result3.change + '円です。');
}
if(result3.error !== null){
  console.log('3つ目でエラーが発生しました:' + result3.error);
}
var result4 = syncBuyApple(result3.change);
if(result4.change !== null){
  console.log('4つ目のおつりは' + result4.change + '円です。');
}
if(result4.error !== null){
  console.log('4つ目でエラーが発生しました:' + result4.error);
}

実行結果

1つ目のおつりは350円です。
2つ目のおつりは200円です。
3つ目のおつりは50円です。
4つ目でエラーが発生しました:100円足りません。

複数行の非同期処理

関数の例 今度はreturnではなく1秒後にコールバックでおつりを受け取る

//150円のりんごを1つ買う関数
//第一引数に支払い金額
//第二引数にコールバック関数
//おつりを計算してコールバック関数に渡す
var asyncBuyApple = function(payment, callback){
  setTimeout(function(){
    if(payment >= 150){
      callback(payment-150, null);
    }else{
      callback(null, '金額が足りません。');
    }
  }, 1000);
}

複数行の非同期処理

//りんごをたくさん買う場合(コールバック地獄)
asyncBuyApple(500, function(change, error){
  if(change !== null){
    console.log('1回目のおつりは' + change + '円です。');
    asyncBuyApple(change, function(change, error){
      if(change !== null){
        console.log('2回目のおつりは' + change + '円です。');

        asyncBuyApple(change, function(change, error){
          if(change !== null){
            console.log('3回目のおつりは' + change + '円です。');
          }
          if(error !== null){
            console.log('3回目でエラーが発生しました:' + error);
          }
        });
      }
      if(error !== null){
        console.log('2回目でエラーが発生しました:' + error);
      }
    });
  }
  if(error !== null){
    console.log('1回目でエラーが発生しました:' + error);
  }
});

実行結果

1つ目のおつりは350円です。
2つ目のおつりは200円です。
3つ目のおつりは50円です。

コールバックを続けて行なうとネストがかなり深くなる. これがコールバック地獄と呼ばれている.

Promise

コールバック地獄を回避する方法として登場したのがPromise.

var promiseBuyApple = function(payment){
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            if (payment >= 150) {
                resolve(payment - 150);
            } else {
                reject('金額が足りません。');
            }
        }, 1000);
  });
}

この関数はPromiseオブジェクトを返す. Promiseオブジェクトのコンストラクタに渡す関数には、成功した場合に実行する関数と、失敗した場合に実行する関数を渡す. この例の場合は、成功した場合にはresolve、失敗した場合にはrejectが実行される.

Promise.prototype.then() メソッドと Promise.prototype.catch() メソッドもまた Promise を返すので、これらをチェーン (連鎖) させることができる.

Promiseを使い簡潔になった例

promiseBuyApple(500).then(function(change){
    console.log('おつりは' + change + '円です');
    // 成功なら次のthenへ
    // 失敗ならcatchへ
    return promiseBuyApple(change);
}).then(function(change){
    console.log('おつりは' + change + '円です');
    // 成功なら次のthenへ
    // 失敗ならcatchへ
    return promiseBuyApple(change);
}).then(function(change){
    console.log('おつりは' + change + '円です');
    // 成功なら次のthenへ
    // 失敗ならcatchへ
    return promiseBuyApple(change);
}).catch(function(error){
  console.log('エラーが発生しました:' + error);
})

このように同期処理のように記述できる.

async / await

Promiseよりさらに非同期処理を簡潔に書ける方法.

async / awaitで書き換えた例

async function asyncCall() {
  try {
    var change = await promiseBuyApple(500);
    console.log('おつりは' + await promiseBuyApple(500) + '円です');
    change = await promiseBuyApple(change);
    console.log('おつりは' + change + '円です');
    change = await promiseBuyApple(change);
    console.log('おつりは' + change + '円です');
    change = await promiseBuyApple(change);
    console.log('おつりは' + change + '円です'); // error: catchへ
  } catch (error) {
    console.log(error)
  }
}

asyncで定義した関数は、promise(Promiseのインスタンス)を返す. awaitをPromiseに付けると、Promiseがresolve()するのを待ってその値を取得する (ように見える) ようになる.awaitはasync関数内でないと使えない.

参考

【MMD】 3Dモデリング 初心者備忘録

今回の使用ソフト

  • metasequoiaLE 3.0

    • モデリングソフト。windowsでしか動きません。macで作りたかったのですが、macで動作するバージョンのものはモデルファイルの出力形式が限られています。。とりあえずフリーでも一通り作れる用になりたかったため今回はこれ。
  • PmxEditor

    • ボーン(骨)とウェイト(肉と骨の繋ぎ役)をモデルにはめるために使いました。今回は、配布モデルのボーンをお借りしてウェイト付けをしました。実は配布されているものをそのまま解すると、色々とエラーメッセージが出てきます。これは、各ファイルに権限が制限されているためのようで、対象ファイルの詳細表示を行い、権限の承認を行わなければいけません。因みに私は面倒くさかったので、ファイルをZIP化->解凍を行いました。(これでも権限による問題は解決されます。詳しくはググってね。)
  • MMD

    • MikuMikuDance。モデルとモーションを合わせて実際に動かすことが出来ます。

各所ポイント

  • metasequoiaLE

    • 鏡像を利用すること。目とか足・腕とか片方を作るともう片方も鏡として自動生成してくれる機能がある。非常に便利。最後にフリーズ?させて鏡じゃなく、実体として保存する。
  • PmxEditor

    • ボーンとモデルの頂点をそろえるため、モデルの頂点を選択し、オブジェクト操作で重心をとり、スケール(0,0,0)を押す。ボーンとモデルを合わせるのはこれが一番簡単そう。
    • ウェイト付けなども鏡像への適用が出来るので、便利。
  • MMD 今のところ特になしかな。

完成品

参考

【express + swagger + vue】swaggerで立てたモックをGUI操作で簡単に変更する

概要

前回【Swagger】モックサーバーの立て方について書きました。

ただ、あくまで定義ファイルという立ち位置だとすると、ワンパターンのAPIでは少し不便です。

FE観点として実際に利用するとなると、例えば複数のコンテンツのUI、もしくは、様々なパターンによってUIを出し分ける実装の検証などには、データの個数・値の変更を行えればなお良いかと思います。

そこで、簡易的ではありますがプロトタイプを作成したのでご紹介します。

仕様

今回用いるフレームワークは以下です。

  • vue.js

  • express

手順としては、

  • expressで、swaggerのモックサーバーにHTTPリクエストを行う
  • 受け取ったAPIをGETリクエス
  • client側のvue.jsで取得と加工を行い、適宜POST
  • expressでPOSTを受け取り、GETリクエス

簡易的ですが、ざっくりと構造は以下となります。

nodejs-server
└ ...api
client
└ ...vue.js
server
└ index.js

server / index.js

const express = require('express')
const app = express()
const port = 3000
const rp = require('request-promise')

// body parser
app.use(express.json())
app.use(express.urlencoded({ extended: true }));

app.use('/', express.static('./client/dist'))

// CORSを許可
app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

// request options
const options = {
  url: 'http://localhost:8080/v2/pet/1',
  method: 'GET'
};

let Pet = {};

(async () => {
  try {
    Pet = await rp(options);
    app.get('/v2/pet/1', function (req, res) {
      res.send(Pet)
    })
  } catch (error) {
    console.log(error);
  }
})();

app.post('/test', function (req, res) {
  Pet = req.body
})

app.listen(process.env.PORT || port);
  1. swaggerのエンドポイントhttp://localhost:8080/v2/pet/1へリクエストを飛ばす
  2. 結果を非同期で取得、http://localhost:3000/v2/pet/1エンドポイントで配信
  3. /testに対し、リクエストが来たら変数Petへ格納、連動しhttp://localhost:3000/v2/pet/1が更新される。

というのがざっくりとした流れです。

client / src / App.vue

<template>
  <div id="app">
    <p>status: {{Response.status}}</p>
    <p>
      status change =>
      <input type="checkbox" v-model="isCheck" />
      <button @click="postData">submit</button>
    </p>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "app",
  components: {},
  data() {
    return {
      Response: {},
      isCheck: false
    };
  },
  methods: {
    async postData() {
      const sendData = {
        ...this.Response,
        status: this.isCheck ? "available" : "unavailable"
      };
      const res = await axios.post("http://localhost:3000/test", sendData);
      console.log(res.data);
    }
  },
  async mounted() {
    try {
      const res = await axios.get("http://localhost:3000/v2/pet/1");
      this.Response = res.data;
      this.isCheck = this.Response.status === "available";
    } catch (err) {
      console.log(err);
    }
  }
};
</script>

vueでは大したことはやっていないのですが

  1. mountedでAPI取得
  2. v-modelでcheckboxと連動して変数の値を変更
  3. 取得したAPIの雛形をもとにcheckboxによって値を更新し、postする

といった流れとなっております。

まとめ

簡単さとシンプルさを追求した分、無理矢理感はありますがAPI定義を元に自由に値を変更できる実装の紹介でした。 一方で、自由すぎる部分や、API定義の変更に弱い部分がまだあると思うのでもう少し良い方法やツールがあればぜひ教えて欲しいです。

リポジトリ

github.com

【Swagger】モックサーバーの立て方

yamlで定義したAPIのモックサーバーの立て方を調べたのでメモ。


1 Swagger Editorにアクセス。

f:id:h-sasaki-worksfront:20191124031137p:plain
swagger.io

今回はデフォルトのPetstoreを利用する。

上部の Generate Server から、今回は nodejs-server を利用する。

クリックするとZIPが落ちるので、任意の箇所へ解凍する。


2 nodejs-server-server/ へ移動し、package.jsonをインストール。


3 yarn start などで、サーバーを立てる。

Your server is listening on port 8080 (http://localhost:8080)
Swagger-ui is available on http://localhost:8080/docs

上記が表示されていればOK。 http://localhost:8080/docs にアクセスすれば、Swagger-uiが表示される。 また、mockのjsonは例えば http://localhost:8080/v2/pet/1 などでアクセスすれば表示が確認できた。

以上。

Instagram APIを叩いて、自分の投稿画像を引っ張ってみた。

インスタグラムAPIを叩く

instagram APIを叩くには、実はかなり工程や条件があります。

インスタがfacebookに吸収されたこともあり、facebookとのアカウント連携なども必要なので、結構手間です。

とりあえず、下記のブログを参考にさせていただいたおかげで かなり順調に進みました。

arrown-blog.com

つまづいたところ

それでもAPIは仕様変更やアップデートされるので、つまづく箇所は出てきます。 つまづきと解消の方法を下記で紹介いたします。

ユーザーアクセストークンの設定画面が新しくなっている

https://developers.facebook.com/tools/exploreで、ユーザーアクセストークンを取得すると思うのですが、画面が新しくなってしまっており、つまづきました。。。

下記の赤枠を押すことで、古い画面に戻ります。

これで、旧手順を踏むことが可能になります。

 

f:id:h-sasaki-worksfront:20191111014554p:plain
古い画面に変更する

実は基本的にはこれだけでした。 あとはブログを参考にすればOKです。

最終的には下記のような感じになりました。(XXXXXはユーザー情報なので伏せてます) limitを設定しない場合は、デフォルトで25個のアイテムが帰ってくるようです。 例えば、投稿数が100未満であれば、とりあえずlimit(100)にしておけば全て取得できました。

APIは期間的な制限はないものを取得しましたが、1日あたりのアクセス制限はあるのでキャッシュなどをうまく利用するといいかもしれないです。

https://graph.facebook.com/v4.0/XXXXX?fields=name,media.limit(100){caption,like_count,media_url,permalink,timestamp,username}&access_token='XXXXX&access_token=XXXXX

以上です。😼

転職活動① ~エージェント会社選定編~

注意

※以下は個人的な解釈でしかないので、相性や職によって大きく差は出るはず。

本題

"フロントエンドエンジニア エージェント" で検索を掛けて出てきたものは大体以下の会社だった。

mynavi-agent.jp

doda.jp

www.r-agent.com

  • ワークポート

転職エージェントは決定人数業界トップクラスの『ワークポート』

  • type

type.career-agent.jp

では、最近転職した同期の意見やネットでの評判をもとに以下に情報をまとめていく。

情報まとめ

  • ワークポート
    ウェブ系に弱く、話があまり通じないのが辛い(同期)

  • type
    専門領域ちゃんと理解してくれてる人が対応してくれてので、話がすごい通じやすかった感じ(同期) 知人紹介で5万円キャンペーンをやっているすごい。

まとめ

typeで実際に転職成功した同期も大企業に入ったし、5万円なのでとりあえず登録した。 あとは、dodaは評判が良さそうなのと 自分で探す方はGreen(転職サイト)を使おうと思う。

ここまでが微妙だった場合は レバテックとパソナキャリアあたりも登録してもいいかもしれない。

とりあえず、次は自分で面談して温度感を確かめよう。

以上。