LoginSignup
11
13

More than 5 years have passed since last update.

Live2Dで複数Canvasにモデル表示

Last updated at Posted at 2015-02-13

Live2DのWebGL版で複数のCanvas表示が可能になっています。
SDKサンプルコードのSimpleプロジェクトをクラス拡張すると、複数表示可能です。

複数CanvasにLive2Dモデルを表示する

1)まずはLive2Dモデルファイルをassets配下に置きます

2015-02-13_13h09_44.png

2)HTML側のCanvasを複数にします

simple.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
            <title>Live2D Simple</title>
        </meta>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=4.0">
        </meta>
    </head>
    <body onload="Simple()">
        <canvas id="glcanvas1" width="300" height="300" style="border:dashed 1px #CCC"></canvas>
        <canvas id="glcanvas2" width="300" height="300" style="border:dashed 1px #CCC"></canvas>
        <!-- Live2D Library -->
        <script src="../../lib/live2d.min.js"></script>
        <!-- User's Script -->
        <script src="src/Simple.js"></script>
    </body>
</html>

3)JavaScript側をクラス拡張します

Simple.js
/**
* モデル定義
*/
var MODEL_DEF = [];
// haruモデル
MODEL_DEF['haru'] = {
    "name":"haru",
    "model":"assets/haru/haru.moc",
    "textures":[
        "assets/haru/haru.1024/texture_00.png",
        "assets/haru/haru.1024/texture_01.png",
        "assets/haru/haru.1024/texture_02.png",
    ]
};
// wankoモデル
MODEL_DEF['wanko'] = {
    "name":"wanko",
    "model":"assets/wanko/wanko.moc",
    "textures":[
        "assets/wanko/wanko.1024/texture_00.png",
    ]
};

/**
* ロード時にクラスを生成する
*/
window.onload = function(){
    // 引数はcanvasID, Model定義, 表示スケール(省略可)
    var glCanvas1 = new Simple("glcanvas1", MODEL_DEF['haru']);
    var glCanvas2 = new Simple("glcanvas2", MODEL_DEF['wanko'] , 2.8);
}


// JavaScriptで発生したエラーを取得
window.onerror = function(msg, url, line, col, error) {
    var errmsg = "file:" + url + " line:" + line + " " + msg;
    console.log(errmsg);
}

/**
* 既存のSimpleを拡張したクラス
*/
var Simple = function(canvasid , modeldef, modelscale) {
    // optional
    if(modelscale == null) modelscale = 2.0;
    console.log("--> Simple()");

    /**
    * Live2Dモデルのインスタンス
    */
    this.live2DModel = null;

    /**
    * アニメーションを停止するためのID
    */
    this.requestID = null;

    /**
    * モデルのロードが完了したら true
    */
    this.loadLive2DCompleted = false;

    /**
    * モデルの初期化が完了したら true
    */
    this.initLive2DCompleted = false;

    /**
    * WebGL Image型オブジェクトの配列
    */
    this.loadedImages = [];

    /**
    * Live2D モデル設定。
    */
    this.modelDef = modeldef;
    /**
    * Live2DモデルOpenGL表示サイズ
    */
    this.modelscale = modelscale;

    // Live2Dの初期化
    Live2D.init();


    // canvasオブジェクトを取得
    this.canvas = document.getElementById(canvasid);

    // コンテキストを失ったとき
    this.canvas.addEventListener("webglcontextlost", function(e) {
        console.log("context lost");
        this.loadLive2DCompleted = false;
        this.initLive2DCompleted = false;

        var cancelAnimationFrame =
            window.cancelAnimationFrame ||
            window.mozCancelAnimationFrame;
        cancelAnimationFrame(requestID); //アニメーションを停止

        e.preventDefault();
    }, false);

    // コンテキストが復元されたとき
    this.canvas.addEventListener("webglcontextrestored" , function(e){
        console.log("webglcontext restored");
        this.initLoop(this.canvas);
    }, false);

    // Init and start Loop
    this.initLoop(this.canvas);
};


/**
* WebGLコンテキストを取得・初期化。
* Live2Dの初期化、描画ループを開始。
*/
Simple.prototype.initLoop = function(canvas/*HTML5 canvasオブジェクト*/)
{
    console.log("--> initLoop");

    //------------ WebGLの初期化 ------------

    // WebGLのコンテキストを取得する
    var gl = this.getWebGLContext(canvas);
    if (!gl) {
        console.log("Failed to create WebGL context.");
        return;
    }

    // 描画エリアを白でクリア
    gl.clearColor( 0.0 , 0.0 , 0.0 , 0.0 );

    //------------ Live2Dの初期化 ------------
    // コールバック対策用
    var that = this;
    // mocファイルからLive2Dモデルのインスタンスを生成
    this.loadBytes(that.modelDef.model, function(buf){
        that.live2DModel = Live2DModelWebGL.loadModel(buf);
    });

    // テクスチャの読み込み
    var loadCount = 0;
    for(var i = 0; i < that.modelDef.textures.length; i++){
        (function ( tno ){// 即時関数で i の値を tno に固定する(onerror用)
            that.loadedImages[tno] = new Image();
            that.loadedImages[tno].src = that.modelDef.textures[tno];
            that.loadedImages[tno].onload = function(){
                if((++loadCount) == that.modelDef.textures.length) {
                    that.loadLive2DCompleted = true;//全て読み終わった
                }
            }
            that.loadedImages[tno].onerror = function() {
                console.log("Failed to load image : " + that.modelDef.textures[tno]);
            }
        })( i );
    }

    //------------ 描画ループ ------------

    (function tick() {
        that.draw(gl, that); // 1回分描画

        var requestAnimationFrame =
            window.requestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.msRequestAnimationFrame;
        that.requestID = requestAnimationFrame( tick , that.canvas );// 一定時間後に自身を呼び出す
    })();
};


Simple.prototype.draw = function(gl/*WebGLコンテキスト*/, that)
{
    // Canvasをクリアする
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Live2D初期化
    if( ! that.live2DModel || ! that.loadLive2DCompleted )
        return; //ロードが完了していないので何もしないで返る

    // ロード完了後に初回のみ初期化する
    if( ! that.initLive2DCompleted ){
        that.initLive2DCompleted = true;

        // 画像からWebGLテクスチャを生成し、モデルに登録
        for( var i = 0; i < that.loadedImages.length; i++ ){
            //Image型オブジェクトからテクスチャを生成
            var texName = that.createTexture(gl, that.loadedImages[i]);

            that.live2DModel.setTexture(i, texName); //モデルにテクスチャをセット
        }

        // テクスチャの元画像の参照をクリア
        that.loadedImages = null;

        // OpenGLのコンテキストをセット
        that.live2DModel.setGL(gl);

        // 表示位置を指定するための行列を定義する
        var s = that.modelscale / that.live2DModel.getCanvasWidth(); //canvasの横幅を-1..1区間に収める
        var matrix4x4 = [
             s, 0, 0, 0,
             0,-s, 0, 0,
             0, 0, 1, 0,
            -that.modelscale/2, that.modelscale/2, 0, 1
        ];
        that.live2DModel.setMatrix(matrix4x4);
    }

    // キャラクターのパラメータを適当に更新
    var t = UtSystem.getTimeMSec() * 0.001 * 2 * Math.PI; //1秒ごとに2π(1周期)増える
    var cycle = 3.0; //パラメータが一周する時間(秒)
    // PARAM_ANGLE_Xのパラメータが[cycle]秒ごとに-30から30まで変化する
    that.live2DModel.setParamFloat("PARAM_ANGLE_Z", 30 * Math.sin(t/cycle));

    // Live2Dモデルを更新して描画
    that.live2DModel.update(); // 現在のパラメータに合わせて頂点等を計算
    that.live2DModel.draw();    // 描画
};


/**
* WebGLのコンテキストを取得する
*/
Simple.prototype.getWebGLContext = function(canvas/*HTML5 canvasオブジェクト*/)
{
    var NAMES = [ "webgl" , "experimental-webgl" , "webkit-3d" , "moz-webgl"];

    var param = {
        alpha : true,
    };

    for( var i = 0; i < NAMES.length; i++ ){
        try{
            var ctx = canvas.getContext( NAMES[i], param );
            if( ctx ) return ctx;
        }
        catch(e){}
    }
    return null;
};


/**
* Image型オブジェクトからテクスチャを生成
*/
Simple.prototype.createTexture = function(gl/*WebGLコンテキスト*/, image/*WebGL Image*/)
{
    var texture = gl.createTexture(); //テクスチャオブジェクトを作成する
    if ( !texture ){
        console.log("Failed to generate gl texture name.");
        return -1;
    }

    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);    //imageを上下反転
    gl.activeTexture( gl.TEXTURE0 );
    gl.bindTexture( gl.TEXTURE_2D , texture );
    gl.texImage2D( gl.TEXTURE_2D , 0 , gl.RGBA , gl.RGBA , gl.UNSIGNED_BYTE , image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
    gl.generateMipmap(gl.TEXTURE_2D);

    return texture;
};


/**
* ファイルをバイト配列としてロードする
*/
Simple.prototype.loadBytes = function(path , callback)
{
    var request = new XMLHttpRequest();
    request.open("GET", path , true);
    request.responseType = "arraybuffer";
    request.onload = function(){
        switch( request.status ){
        case 200:
            callback( request.response );
            break;
        default:
            console.log( "Failed to load (" + request.status + ") : " + path );
            break;
        }
    }

    request.send(null);
};

4)実行結果

こんなかんじになりました~!
1.gif

11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13